summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp21
-rw-r--r--apex/jobscheduler/service/aconfig/job.aconfig7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java404
-rw-r--r--core/api/current.txt59
-rw-r--r--core/api/removed.txt60
-rw-r--r--core/api/system-current.txt13
-rw-r--r--core/api/system-removed.txt122
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/Android.bp33
-rw-r--r--core/java/android/app/ActivityManager.java4
-rw-r--r--core/java/android/app/ActivityManagerInternal.java3
-rw-r--r--core/java/android/app/ActivityThread.java18
-rw-r--r--core/java/android/app/LoadedApk.java2
-rw-r--r--core/java/android/app/Notification.java6
-rw-r--r--core/java/android/app/OWNERS1
-rw-r--r--core/java/android/app/appfunctions/AppFunctionManager.java34
-rw-r--r--core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java13
-rw-r--r--core/java/android/app/appfunctions/AppFunctionService.java74
-rw-r--r--core/java/android/app/notification.aconfig8
-rw-r--r--core/java/android/app/usage/UsageEventsQuery.java2
-rw-r--r--core/java/android/companion/AssociationInfo.java2
-rw-r--r--core/java/android/companion/AssociationRequest.java4
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java19
-rw-r--r--core/java/android/content/Context.java8
-rw-r--r--core/java/android/content/Intent.java45
-rw-r--r--core/java/android/content/pm/ActivityInfo.java14
-rw-r--r--core/java/android/content/pm/IPackageInstaller.aidl6
-rw-r--r--core/java/android/content/pm/PackageInstaller.java135
-rw-r--r--core/java/android/content/pm/flags.aconfig16
-rw-r--r--core/java/android/content/pm/multiuser.aconfig12
-rw-r--r--core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl2
-rw-r--r--core/java/android/content/pm/verify/pkg/VerificationSession.java54
-rw-r--r--core/java/android/content/pm/xr.aconfig9
-rw-r--r--core/java/android/hardware/input/input_framework.aconfig7
-rw-r--r--core/java/android/hardware/radio/RadioAlert.aidl20
-rw-r--r--core/java/android/hardware/radio/RadioAlert.java505
-rw-r--r--core/java/android/os/BaseBundle.java4
-rw-r--r--core/java/android/os/Build.java215
-rw-r--r--core/java/android/os/Bundle.java11
-rw-r--r--core/java/android/os/CombinedVibration.java62
-rw-r--r--core/java/android/os/Debug.java19
-rw-r--r--core/java/android/os/Process.java2
-rw-r--r--core/java/android/os/RemoteCallbackList.java470
-rw-r--r--core/java/android/os/VibrationEffect.java26
-rw-r--r--core/java/android/os/vibrator/PrebakedSegment.java107
-rw-r--r--core/java/android/os/vibrator/PrimitiveSegment.java10
-rw-r--r--core/java/android/os/vibrator/VibrationConfig.java20
-rw-r--r--core/java/android/os/vibrator/VibrationEffectSegment.java22
-rw-r--r--core/java/android/os/vibrator/flags.aconfig23
-rw-r--r--core/java/android/security/forensic/IForensicService.aidl32
-rw-r--r--core/java/android/security/forensic/IForensicServiceCommandCallback.aidl33
-rw-r--r--core/java/android/security/forensic/IForensicServiceStateCallback.aidl31
-rw-r--r--core/java/android/text/Layout.java25
-rw-r--r--core/java/android/util/NtpTrustedTime.java18
-rw-r--r--core/java/android/view/InsetsController.java4
-rw-r--r--core/java/android/view/SurfaceView.java4
-rw-r--r--core/java/android/view/ViewProtoLogGroups.java42
-rw-r--r--core/java/android/view/ViewRootImpl.java41
-rw-r--r--core/java/android/view/accessibility/flags/accessibility_flags.aconfig10
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java30
-rw-r--r--core/java/android/view/inputmethod/flags.aconfig11
-rw-r--r--core/java/android/window/BackEvent.java16
-rw-r--r--core/java/android/window/DesktopModeFlags.java2
-rw-r--r--core/java/android/window/TaskSnapshot.java7
-rw-r--r--core/java/android/window/WindowContainerTransaction.java54
-rw-r--r--core/java/android/window/WindowOnBackInvokedDispatcher.java4
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig14
-rw-r--r--core/java/com/android/internal/app/ResolverListAdapter.java7
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java4
-rw-r--r--core/java/com/android/internal/widget/NotificationProgressBar.java49
-rw-r--r--core/java/com/android/internal/widget/NotificationProgressDrawable.java21
-rw-r--r--core/java/com/android/internal/widget/NotificationRowIconView.java224
-rw-r--r--core/jni/android_hardware_camera2_CameraMetadata.cpp18
-rw-r--r--core/res/AndroidManifest.xml12
-rw-r--r--core/res/res/color-watch-v36/btn_material_filled_background_color.xml23
-rw-r--r--core/res/res/color-watch-v36/btn_material_filled_text_color.xml23
-rw-r--r--core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml (renamed from packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml)18
-rw-r--r--core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml23
-rw-r--r--core/res/res/drawable-watch-v36/btn_background_material_filled.xml28
-rw-r--r--core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml28
-rw-r--r--core/res/res/drawable/ic_zen_mode_icon_piano.xml25
-rw-r--r--core/res/res/drawable/notification_progress.xml37
-rw-r--r--core/res/res/layout/notification_template_material_progress.xml7
-rw-r--r--core/res/res/layout/notification_template_notification_progress_bar.xml (renamed from packages/SystemUI/res/layout/udfps_bp_view.xml)15
-rw-r--r--core/res/res/values-watch-v36/colors.xml18
-rw-r--r--core/res/res/values-watch-v36/config.xml20
-rw-r--r--core/res/res/values-watch-v36/dimens_material.xml28
-rw-r--r--core/res/res/values-watch-v36/styles_material.xml58
-rw-r--r--core/res/res/values/config.xml25
-rw-r--r--core/res/res/values/dimens.xml16
-rw-r--r--core/res/res/values/styles_material.xml4
-rw-r--r--core/res/res/values/symbols.xml11
-rw-r--r--core/tests/coretests/src/android/app/NotificationTest.java17
-rw-r--r--core/tests/coretests/src/android/app/OWNERS2
-rw-r--r--core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java47
-rw-r--r--core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java6
-rw-r--r--core/tests/coretests/src/android/os/OWNERS3
-rw-r--r--core/tests/coretests/src/android/text/LayoutTest.java54
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java38
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java1
-rw-r--r--core/tests/vibrator/src/android/os/CombinedVibrationTest.java173
-rw-r--r--core/tests/vibrator/src/android/os/VibrationEffectTest.java46
-rw-r--r--core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java93
-rw-r--r--core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java23
-rw-r--r--core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java7
-rw-r--r--core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java6
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--errorprone/Android.bp1
-rw-r--r--errorprone/OWNERS3
-rw-r--r--errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java6
-rw-r--r--graphics/java/android/graphics/text/PositionedGlyphs.java39
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java3
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt5
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt8
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt37
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml4
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml8
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java82
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt133
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java63
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java44
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java78
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java87
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt111
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt52
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt347
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt156
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt57
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java22
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java60
-rw-r--r--libs/appfunctions/api/current.txt5
-rw-r--r--libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java27
-rw-r--r--libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java71
-rw-r--r--libs/hwui/DamageAccumulator.cpp84
-rw-r--r--libs/hwui/hwui/Bitmap.cpp14
-rw-r--r--libs/hwui/jni/text/TextShaper.cpp97
-rw-r--r--media/java/android/media/IMediaRouterService.aidl3
-rw-r--r--media/java/android/media/MediaRouter2.java14
-rw-r--r--media/java/android/media/MediaRouter2Manager.java10
-rw-r--r--packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java2
-rw-r--r--packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java2
-rw-r--r--packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt2
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/non_collapsing_toolbar_content_layout.xml4
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml13
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml13
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes_bridge.xml176
-rw-r--r--packages/SettingsLib/IllustrationPreference/Android.bp1
-rw-r--r--packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java2
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml2
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml2
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml2
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml6
-rw-r--r--packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml4
-rw-r--r--packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java3
-rw-r--r--packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt38
-rw-r--r--packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt24
-rw-r--r--packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt31
-rw-r--r--packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt206
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt2
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt104
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt8
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SuggestionCard.kt166
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt8
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsBannerTest.kt (renamed from packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsBannerTest.kt)0
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsCollapsibleBannerTest.kt (renamed from packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleBannerTest.kt)0
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SuggestionCardTest.kt84
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt91
-rw-r--r--packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt2
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig10
-rw-r--r--packages/SettingsLib/res/values/strings.xml5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java16
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/StatementService/Parser/Android.bp26
-rw-r--r--packages/StatementService/Parser/AndroidManifest.xml19
-rw-r--r--packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt85
-rw-r--r--packages/StatementService/TEST_MAPPING7
-rw-r--r--packages/StatementService/tests/Android.bp30
-rw-r--r--packages/StatementService/tests/AndroidManifest.xml27
-rw-r--r--packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt78
-rw-r--r--packages/SystemUI/Android.bp24
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig30
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt20
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt108
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt67
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt11
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt28
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt21
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt9
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt56
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt47
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt21
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt4
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt1
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt10
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt21
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt173
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt29
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementMatcherTest.kt56
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt4
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt1
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt6
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt41
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt135
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt6
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt65
-rw-r--r--packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt4
-rw-r--r--packages/SystemUI/docs/scene.md3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java17
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt70
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java1067
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java161
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java197
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt643
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt70
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt)59
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt42
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt36
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt118
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt158
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt96
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt104
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt39
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt)42
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt121
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt)160
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt136
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt85
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt113
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java259
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt21
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt140
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt29
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt31
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt94
-rw-r--r--packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml5
-rw-r--r--packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml17
-rw-r--r--packages/SystemUI/res/layout/hearing_tool_item.xml10
-rw-r--r--packages/SystemUI/res/layout/ongoing_activity_chip.xml33
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml6
-rw-r--r--packages/SystemUI/res/layout/super_notification_shade.xml16
-rw-r--r--packages/SystemUI/res/layout/udfps_fpm_empty_view.xml30
-rw-r--r--packages/SystemUI/res/layout/udfps_view.xml31
-rw-r--r--packages/SystemUI/res/values-land/config.xml6
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/config.xml6
-rw-r--r--packages/SystemUI/res/values-sw600dp-port/config.xml6
-rw-r--r--packages/SystemUI/res/values/config.xml24
-rw-r--r--packages/SystemUI/res/values/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/styles.xml16
-rw-r--r--packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java843
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconView.java263
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt218
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt555
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java330
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt117
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt72
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt108
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt78
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt352
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt184
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt147
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/OWNERS4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt)37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt (renamed from packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt)10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt84
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt92
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt70
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt121
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java95
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt103
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt86
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt179
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java134
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt75
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt73
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt83
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt4
-rw-r--r--packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml19
-rw-r--r--packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml20
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java245
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java414
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt149
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt351
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt289
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt109
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt97
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java51
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java26
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt49
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt)6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt13
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt41
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt39
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt37
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt20
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt83
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt3
-rw-r--r--packages/Vcn/OWNERS6
-rw-r--r--ravenwood/Android.bp1
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java29
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java3
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java3
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java155
-rw-r--r--ravenwood/tests/bivalenttest/Android.bp62
-rw-r--r--ravenwood/tests/minimum-test/Android.bp (renamed from ravenwood/minimum-test/Android.bp)0
-rw-r--r--ravenwood/tests/minimum-test/README.md (renamed from ravenwood/minimum-test/README.md)0
-rw-r--r--ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java (renamed from ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java)0
-rw-r--r--ravenwood/tests/mockito/Android.bp (renamed from ravenwood/mockito/Android.bp)0
-rw-r--r--ravenwood/tests/mockito/AndroidManifest.xml (renamed from ravenwood/mockito/AndroidManifest.xml)0
-rw-r--r--ravenwood/tests/mockito/AndroidTest.xml (renamed from ravenwood/mockito/AndroidTest.xml)0
-rw-r--r--ravenwood/tests/mockito/README.md (renamed from ravenwood/mockito/README.md)0
-rw-r--r--ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java (renamed from ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java)0
-rw-r--r--ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java (renamed from ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java)0
-rw-r--r--ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java (renamed from ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java)0
-rw-r--r--ravenwood/tests/resapk_test/Android.bp (renamed from ravenwood/resapk_test/Android.bp)0
-rw-r--r--ravenwood/tests/resapk_test/apk/Android.bp (renamed from ravenwood/resapk_test/apk/Android.bp)0
-rw-r--r--ravenwood/tests/resapk_test/apk/AndroidManifest.xml (renamed from ravenwood/resapk_test/apk/AndroidManifest.xml)0
-rw-r--r--ravenwood/tests/resapk_test/apk/res/values/strings.xml (renamed from ravenwood/resapk_test/apk/res/values/strings.xml)0
-rw-r--r--ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java (renamed from ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java)0
-rw-r--r--ravenwood/tests/runtime-test/Android.bp (renamed from ravenwood/runtime-test/Android.bp)0
-rw-r--r--ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java (renamed from ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java)0
-rw-r--r--ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java (renamed from ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java)0
-rw-r--r--ravenwood/tests/services-test/Android.bp (renamed from ravenwood/services-test/Android.bp)0
-rw-r--r--ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java (renamed from ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java)0
-rw-r--r--ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java (renamed from ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java)0
-rw-r--r--ravenwood/tools/hoststubgen/.gitignore4
-rw-r--r--ravenwood/tools/hoststubgen/Android.bp (renamed from tools/hoststubgen/hoststubgen/Android.bp)0
-rw-r--r--ravenwood/tools/hoststubgen/README.md (renamed from tools/hoststubgen/README.md)21
-rw-r--r--ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java (renamed from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java)0
-rw-r--r--ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java (renamed from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java)0
-rw-r--r--ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java (renamed from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java)0
-rw-r--r--ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java (renamed from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java)0
-rw-r--r--ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java (renamed from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java)0
-rw-r--r--ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java (renamed from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java)0
-rw-r--r--ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java (renamed from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java)0
-rw-r--r--ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java (renamed from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java)0
-rw-r--r--ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java (renamed from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java)0
-rw-r--r--ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java (renamed from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java)0
-rw-r--r--ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java (renamed from tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java)0
-rw-r--r--ravenwood/tools/hoststubgen/common.sh (renamed from tools/hoststubgen/common.sh)0
-rw-r--r--ravenwood/tools/hoststubgen/framework-policy-override.txt (renamed from tools/hoststubgen/hoststubgen/framework-policy-override.txt)0
-rw-r--r--ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java (renamed from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java)0
-rw-r--r--ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java (renamed from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java)0
-rw-r--r--ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java (renamed from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java)0
-rw-r--r--ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java (renamed from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java)0
-rw-r--r--ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java (renamed from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java)0
-rw-r--r--ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java (renamed from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java)0
-rw-r--r--ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java (renamed from tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java)0
-rw-r--r--ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt (renamed from tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt)0
-rw-r--r--ravenwood/tools/hoststubgen/invoketest/Android.bp (renamed from tools/hoststubgen/hoststubgen/invoketest/Android.bp)0
-rwxr-xr-xravenwood/tools/hoststubgen/invoketest/hoststubgen-invoke-test.sh (renamed from tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh)0
-rw-r--r--ravenwood/tools/hoststubgen/jarjar-rules.txt (renamed from tools/hoststubgen/hoststubgen/jarjar-rules.txt)0
-rw-r--r--ravenwood/tools/hoststubgen/scripts/Android.bp (renamed from tools/hoststubgen/scripts/Android.bp)0
-rwxr-xr-xravenwood/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh (renamed from tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh)0
-rwxr-xr-xravenwood/tools/hoststubgen/scripts/dump-jar (renamed from tools/hoststubgen/scripts/dump-jar)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt (renamed from tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp)2
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/AndroidTest-host.xml (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/README.md (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/README.md)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt)0
-rwxr-xr-xravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt)0
-rwxr-xr-xravenwood/tools/hoststubgen/test-tiny-framework/run-test-manually.sh (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh)0
-rwxr-xr-xravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java (renamed from tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java)0
-rw-r--r--ravenwood/tools/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt (renamed from tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt (renamed from tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt (renamed from tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt)0
-rw-r--r--ravenwood/tools/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt (renamed from tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt)0
-rw-r--r--services/Android.bp16
-rw-r--r--services/accessibility/accessibility.aconfig15
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java13
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java3
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java38
-rw-r--r--services/autofill/bugfixes.aconfig7
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java3096
-rw-r--r--services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java9
-rw-r--r--services/core/java/com/android/server/SystemConfig.java52
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java33
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java76
-rw-r--r--services/core/java/com/android/server/am/BroadcastController.java1
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java7
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java8
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java7
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig8
-rw-r--r--services/core/java/com/android/server/display/brightness/BrightnessReason.java7
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java17
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java3
-rw-r--r--services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java15
-rw-r--r--services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java16
-rw-r--r--services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java3
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java11
-rw-r--r--services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java477
-rw-r--r--services/core/java/com/android/server/media/AudioManagerRouteController.java2
-rw-r--r--services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java5
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java17
-rw-r--r--services/core/java/com/android/server/media/MediaRouterService.java12
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java30
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerInternal.java2
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java40
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java6
-rw-r--r--services/core/java/com/android/server/notification/RankingConfig.java2
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig7
-rw-r--r--services/core/java/com/android/server/pinner/PinnerService.java17
-rw-r--r--services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java80
-rw-r--r--services/core/java/com/android/server/pm/BroadcastHelper.java95
-rw-r--r--services/core/java/com/android/server/pm/DexOptHelper.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java40
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java177
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java25
-rw-r--r--services/core/java/com/android/server/pm/verify/pkg/VerifierController.java58
-rw-r--r--services/core/java/com/android/server/power/Notifier.java4
-rw-r--r--services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java3
-rw-r--r--services/core/java/com/android/server/security/forensic/ForensicService.java294
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java114
-rw-r--r--services/core/java/com/android/server/uri/NeededUriGrants.java25
-rw-r--r--services/core/java/com/android/server/vibrator/HalVibration.java39
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java53
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java32
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java180
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java44
-rw-r--r--services/core/java/com/android/server/wm/AppTaskImpl.java2
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java6
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java2
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java69
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java4
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java3
-rw-r--r--services/core/java/com/android/server/wm/Task.java34
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java16
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java7
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java75
-rw-r--r--services/java/com/android/server/SystemServer.java27
-rw-r--r--services/midi/java/com/android/server/midi/MidiService.java6
-rw-r--r--services/people/java/com/android/server/people/data/CallLogQueryHelper.java2
-rw-r--r--services/people/java/com/android/server/people/data/ContactsQueryHelper.java8
-rw-r--r--services/people/java/com/android/server/people/data/MmsQueryHelper.java4
-rw-r--r--services/people/java/com/android/server/people/data/SmsQueryHelper.java2
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt15
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java106
-rw-r--r--services/tests/appfunctions/Android.bp2
-rw-r--r--services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt89
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java64
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java36
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java2
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java9
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java76
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java8
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java3
-rw-r--r--services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java54
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java49
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java1024
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java15
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java3
-rw-r--r--services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java62
-rw-r--r--services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java27
-rw-r--r--services/tests/security/forensic/Android.bp41
-rw-r--r--services/tests/security/forensic/AndroidManifest.xml27
-rw-r--r--services/tests/security/forensic/AndroidTest.xml32
-rw-r--r--services/tests/security/forensic/TEST_MAPPING7
-rw-r--r--services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java387
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigReadOnlyFeaturesTest.kt167
-rw-r--r--services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java3
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java26
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java30
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java106
-rw-r--r--services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java8
-rw-r--r--services/tests/wmtests/Android.bp1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java27
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java55
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java274
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java12
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml6
-rw-r--r--tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt20
-rw-r--r--tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java34
-rw-r--r--tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java50
-rw-r--r--tests/Input/src/android/hardware/input/InputManagerTest.kt26
-rw-r--r--tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt24
-rw-r--r--tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt29
-rw-r--r--tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt33
-rw-r--r--tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt31
-rw-r--r--tests/Input/src/com/android/server/input/BatteryControllerTests.kt43
-rw-r--r--tests/Input/src/com/android/server/input/InputManagerServiceTests.kt6
-rw-r--r--tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt8
-rw-r--r--tests/Input/src/com/android/server/input/KeyRemapperTests.kt32
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt118
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt29
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt35
-rw-r--r--tests/Input/src/com/android/test/input/MockInputManagerRule.kt42
-rw-r--r--tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt2
-rw-r--r--tests/testables/src/android/testing/TestWithLooperRule.java3
-rw-r--r--tools/aapt2/SdkConstants.cpp13
-rw-r--r--tools/hoststubgen/.gitignore3
-rw-r--r--tools/hoststubgen/OWNERS1
-rw-r--r--tools/hoststubgen/TEST_MAPPING7
-rw-r--r--tools/hoststubgen/hoststubgen/.gitignore1
-rw-r--r--tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt13
-rw-r--r--tools/systemfeatures/tests/golden/RoFeatures.java.gen7
-rw-r--r--tools/systemfeatures/tests/golden/RoNoFeatures.java.gen7
-rw-r--r--tools/systemfeatures/tests/golden/RwFeatures.java.gen7
-rw-r--r--tools/systemfeatures/tests/golden/RwNoFeatures.java.gen7
-rw-r--r--tools/systemfeatures/tests/src/ArrayMap.java26
-rw-r--r--tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java8
829 files changed, 17357 insertions, 14485 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index c6ce799f0a24..0080a8d63bfc 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -81,6 +81,7 @@ aconfig_declarations_group {
"android.view.inputmethod.flags-aconfig-java",
"android.webkit.flags-aconfig-java",
"android.widget.flags-aconfig-java",
+ "android.xr.flags-aconfig-java",
"art_exported_aconfig_flags_lib",
"backstage_power_flags_lib",
"backup_flags_lib",
@@ -791,6 +792,12 @@ java_aconfig_library {
],
}
+cc_aconfig_library {
+ name: "android.permission.flags-aconfig-cc",
+ aconfig_declarations: "android.permission.flags-aconfig",
+ host_supported: true,
+}
+
// SQLite
aconfig_declarations {
name: "android.database.sqlite-aconfig",
@@ -893,6 +900,20 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// XR
+aconfig_declarations {
+ name: "android.xr.flags-aconfig",
+ package: "android.xr",
+ container: "system",
+ srcs: ["core/java/android/content/pm/xr.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.xr.flags-aconfig-java",
+ aconfig_declarations: "android.xr.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// android.app
aconfig_declarations {
name: "android.app.flags-aconfig",
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 11c5b51e23ae..98e53ab97872 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -82,3 +82,10 @@ flag {
description: "Applies the normal quota policy to FGS jobs"
bug: "341201311"
}
+
+flag {
+ name: "adjust_quota_default_constants"
+ namespace: "backstage_power"
+ description: "Adjust quota default parameters"
+ bug: "347058927"
+} \ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 03a3a0d51891..885bad5e31c8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -394,13 +394,13 @@ public final class QuotaController extends StateController {
* minutes to run its jobs.
*/
private final long[] mBucketPeriodsMs = new long[]{
- QcConstants.DEFAULT_WINDOW_SIZE_ACTIVE_MS,
- QcConstants.DEFAULT_WINDOW_SIZE_WORKING_MS,
- QcConstants.DEFAULT_WINDOW_SIZE_FREQUENT_MS,
+ QcConstants.DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS,
+ QcConstants.DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS,
+ QcConstants.DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS,
QcConstants.DEFAULT_WINDOW_SIZE_RARE_MS,
0, // NEVER
QcConstants.DEFAULT_WINDOW_SIZE_RESTRICTED_MS,
- QcConstants.DEFAULT_WINDOW_SIZE_EXEMPTED_MS
+ QcConstants.DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS
};
/** The maximum period any bucket can have. */
@@ -454,7 +454,7 @@ public final class QuotaController extends StateController {
*/
private final long[] mEJLimitsMs = new long[]{
QcConstants.DEFAULT_EJ_LIMIT_ACTIVE_MS,
- QcConstants.DEFAULT_EJ_LIMIT_WORKING_MS,
+ QcConstants.DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS,
QcConstants.DEFAULT_EJ_LIMIT_FREQUENT_MS,
QcConstants.DEFAULT_EJ_LIMIT_RARE_MS,
0, // NEVER
@@ -476,7 +476,8 @@ public final class QuotaController extends StateController {
/**
* Length of time used to split an app's top time into chunks.
*/
- private long mEJTopAppTimeChunkSizeMs = QcConstants.DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
+ private long mEJTopAppTimeChunkSizeMs =
+ QcConstants.DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
/**
* How much EJ quota to give back to an app based on the number of top app time chunks it had.
@@ -486,7 +487,7 @@ public final class QuotaController extends StateController {
/**
* How much EJ quota to give back to an app based on each non-top user interaction.
*/
- private long mEJRewardInteractionMs = QcConstants.DEFAULT_EJ_REWARD_INTERACTION_MS;
+ private long mEJRewardInteractionMs = QcConstants.DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS;
/**
* How much EJ quota to give back to an app based on each notification seen event.
@@ -498,14 +499,6 @@ public final class QuotaController extends StateController {
private long mEJGracePeriodTopAppMs = QcConstants.DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
- private long mQuotaBumpAdditionalDurationMs =
- QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS;
- private int mQuotaBumpAdditionalJobCount = QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT;
- private int mQuotaBumpAdditionalSessionCount =
- QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT;
- private long mQuotaBumpWindowSizeMs = QcConstants.DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS;
- private int mQuotaBumpLimit = QcConstants.DEFAULT_QUOTA_BUMP_LIMIT;
-
/**
* List of system apps with the {@link android.Manifest.permission#INSTALL_PACKAGES} permission
* granted for each user.
@@ -578,6 +571,8 @@ public final class QuotaController extends StateController {
} catch (RemoteException e) {
// ignored; both services live in system_server
}
+
+ processQuotaConstantsAdjustment();
}
@Override
@@ -1095,7 +1090,7 @@ public final class QuotaController extends StateController {
// essentially run until they reach the maximum limit.
if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
return calculateTimeUntilQuotaConsumedLocked(
- events, startMaxElapsed, maxExecutionTimeRemainingMs, false);
+ events, startMaxElapsed, maxExecutionTimeRemainingMs);
}
// Need to check both max time and period time in case one is less than the other.
@@ -1104,9 +1099,9 @@ public final class QuotaController extends StateController {
// bucket value.
return Math.min(
calculateTimeUntilQuotaConsumedLocked(
- events, startMaxElapsed, maxExecutionTimeRemainingMs, false),
+ events, startMaxElapsed, maxExecutionTimeRemainingMs),
calculateTimeUntilQuotaConsumedLocked(
- events, startWindowElapsed, allowedTimeRemainingMs, true));
+ events, startWindowElapsed, allowedTimeRemainingMs));
}
/**
@@ -1116,36 +1111,12 @@ public final class QuotaController extends StateController {
* @param deadSpaceMs How much time can be allowed to count towards the quota
*/
private long calculateTimeUntilQuotaConsumedLocked(@NonNull List<TimedEvent> sessions,
- final long windowStartElapsed, long deadSpaceMs, boolean allowQuotaBumps) {
+ final long windowStartElapsed, long deadSpaceMs) {
long timeUntilQuotaConsumedMs = 0;
long start = windowStartElapsed;
- int numQuotaBumps = 0;
- final long quotaBumpWindowStartElapsed =
- sElapsedRealtimeClock.millis() - mQuotaBumpWindowSizeMs;
final int numSessions = sessions.size();
- if (allowQuotaBumps) {
- for (int i = numSessions - 1; i >= 0; --i) {
- TimedEvent event = sessions.get(i);
-
- if (event instanceof QuotaBump) {
- if (event.getEndTimeElapsed() >= quotaBumpWindowStartElapsed
- && numQuotaBumps++ < mQuotaBumpLimit) {
- deadSpaceMs += mQuotaBumpAdditionalDurationMs;
- } else {
- break;
- }
- }
- }
- }
for (int i = 0; i < numSessions; ++i) {
- TimedEvent event = sessions.get(i);
-
- if (event instanceof QuotaBump) {
- continue;
- }
-
- TimingSession session = (TimingSession) event;
-
+ TimingSession session = (TimingSession) sessions.get(i);
if (session.endTimeElapsed < windowStartElapsed) {
// Outside of window. Ignore.
continue;
@@ -1330,41 +1301,15 @@ public final class QuotaController extends StateController {
final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
int sessionCountInWindow = 0;
- int numQuotaBumps = 0;
- final long quotaBumpWindowStartElapsed = nowElapsed - mQuotaBumpWindowSizeMs;
// The minimum time between the start time and the beginning of the events that were
// looked at --> how much time the stats will be valid for.
long emptyTimeMs = Long.MAX_VALUE;
// Sessions are non-overlapping and in order of occurrence, so iterating backwards will get
// the most recent ones.
final int loopStart = events.size() - 1;
- // Process QuotaBumps first to ensure the limits are properly adjusted.
- for (int i = loopStart; i >= 0; --i) {
- TimedEvent event = events.get(i);
-
- if (event.getEndTimeElapsed() < quotaBumpWindowStartElapsed
- || numQuotaBumps >= mQuotaBumpLimit) {
- break;
- }
-
- if (event instanceof QuotaBump) {
- stats.allowedTimePerPeriodMs += mQuotaBumpAdditionalDurationMs;
- stats.jobCountLimit += mQuotaBumpAdditionalJobCount;
- stats.sessionCountLimit += mQuotaBumpAdditionalSessionCount;
- emptyTimeMs = Math.min(emptyTimeMs,
- event.getEndTimeElapsed() - quotaBumpWindowStartElapsed);
- numQuotaBumps++;
- }
- }
TimingSession lastSeenTimingSession = null;
for (int i = loopStart; i >= 0; --i) {
- TimedEvent event = events.get(i);
-
- if (event instanceof QuotaBump) {
- continue;
- }
-
- TimingSession session = (TimingSession) event;
+ TimingSession session = (TimingSession) events.get(i);
// Window management.
if (startWindowElapsed < session.endTimeElapsed) {
@@ -1469,6 +1414,13 @@ public final class QuotaController extends StateController {
}
}
+ void processQuotaConstantsAdjustment() {
+ if (Flags.adjustQuotaDefaultConstants()) {
+ mQcConstants.adjustDefaultBucketWindowSizes();
+ mQcConstants.adjustDefaultEjLimits();
+ }
+ }
+
@VisibleForTesting
void incrementJobCountLocked(final int userId, @NonNull final String packageName, int count) {
final long now = sElapsedRealtimeClock.millis();
@@ -2058,28 +2010,6 @@ public final class QuotaController extends StateController {
}
@VisibleForTesting
- static final class QuotaBump implements TimedEvent {
- // Event timestamp in elapsed realtime timebase.
- public final long eventTimeElapsed;
-
- QuotaBump(long eventElapsed) {
- this.eventTimeElapsed = eventElapsed;
- }
-
- @Override
- public long getEndTimeElapsed() {
- return eventTimeElapsed;
- }
-
- @Override
- public void dump(IndentingPrintWriter pw) {
- pw.print("Quota bump @ ");
- pw.print(eventTimeElapsed);
- pw.println();
- }
- }
-
- @VisibleForTesting
static final class ShrinkableDebits {
/** The amount of quota remaining. Can be negative if limit changes. */
private long mDebitTally;
@@ -2528,21 +2458,6 @@ public final class QuotaController extends StateController {
updateStandbyBucket(userId, packageName, bucketIndex);
});
}
-
- @Override
- public void triggerTemporaryQuotaBump(String packageName, @UserIdInt int userId) {
- synchronized (mLock) {
- List<TimedEvent> events = mTimingEvents.get(userId, packageName);
- if (events == null || events.size() == 0) {
- // If the app hasn't run any jobs, there's no point giving it a quota bump.
- return;
- }
- events.add(new QuotaBump(sElapsedRealtimeClock.millis()));
- invalidateAllExecutionStatsLocked(userId, packageName);
- }
- // Update jobs out of band.
- mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
- }
}
@VisibleForTesting
@@ -3019,7 +2934,6 @@ public final class QuotaController extends StateController {
mQcConstants.mRateLimitingConstantsUpdated = false;
mQcConstants.mExecutionPeriodConstantsUpdated = false;
mQcConstants.mEJLimitConstantsUpdated = false;
- mQcConstants.mQuotaBumpConstantsUpdated = false;
}
@Override
@@ -3046,7 +2960,6 @@ public final class QuotaController extends StateController {
private boolean mRateLimitingConstantsUpdated = false;
private boolean mExecutionPeriodConstantsUpdated = false;
private boolean mEJLimitConstantsUpdated = false;
- private boolean mQuotaBumpConstantsUpdated = false;
/** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
private static final String QC_CONSTANT_PREFIX = "qc_";
@@ -3194,21 +3107,6 @@ public final class QuotaController extends StateController {
@VisibleForTesting
static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS =
QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms";
- @VisibleForTesting
- static final String KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS =
- QC_CONSTANT_PREFIX + "quota_bump_additional_duration_ms";
- @VisibleForTesting
- static final String KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT =
- QC_CONSTANT_PREFIX + "quota_bump_additional_job_count";
- @VisibleForTesting
- static final String KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT =
- QC_CONSTANT_PREFIX + "quota_bump_additional_session_count";
- @VisibleForTesting
- static final String KEY_QUOTA_BUMP_WINDOW_SIZE_MS =
- QC_CONSTANT_PREFIX + "quota_bump_window_size_ms";
- @VisibleForTesting
- static final String KEY_QUOTA_BUMP_LIMIT =
- QC_CONSTANT_PREFIX + "quota_bump_limit";
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
10 * 60 * 1000L; // 10 minutes
@@ -3224,14 +3122,28 @@ public final class QuotaController extends StateController {
10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
30 * 1000L; // 30 seconds
- private static final long DEFAULT_WINDOW_SIZE_EXEMPTED_MS =
+ // Legacy default window size for EXEMPTED bucket
+ private static final long DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS =
DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time
- private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS =
+ // Legacy default window size for ACTIVE bucket
+ private static final long DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS =
DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; // ACTIVE apps can run jobs at any time
- private static final long DEFAULT_WINDOW_SIZE_WORKING_MS =
+ // Legacy default window size for WORKING bucket
+ private static final long DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS =
2 * 60 * 60 * 1000L; // 2 hours
- private static final long DEFAULT_WINDOW_SIZE_FREQUENT_MS =
+ // Legacy default window size for FREQUENT bucket
+ private static final long DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS =
8 * 60 * 60 * 1000L; // 8 hours
+
+ private static final long DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS =
+ 20 * 60 * 1000L; // 20 minutes.
+ private static final long DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS =
+ 30 * 60 * 1000L; // 30 minutes.
+ private static final long DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS =
+ 4 * 60 * 60 * 1000L; // 4 hours
+ private static final long DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS =
+ 12 * 60 * 60 * 1000L; // 12 hours
+
private static final long DEFAULT_WINDOW_SIZE_RARE_MS =
24 * 60 * 60 * 1000L; // 24 hours
private static final long DEFAULT_WINDOW_SIZE_RESTRICTED_MS =
@@ -3245,9 +3157,9 @@ public final class QuotaController extends StateController {
75; // 75/window = 450/hr = 1/session
private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE = DEFAULT_MAX_JOB_COUNT_EXEMPTED;
private static final int DEFAULT_MAX_JOB_COUNT_WORKING = // 120/window = 60/hr = 12/session
- (int) (60.0 * DEFAULT_WINDOW_SIZE_WORKING_MS / HOUR_IN_MILLIS);
+ (int) (60.0 * DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS / HOUR_IN_MILLIS);
private static final int DEFAULT_MAX_JOB_COUNT_FREQUENT = // 200/window = 25/hr = 25/session
- (int) (25.0 * DEFAULT_WINDOW_SIZE_FREQUENT_MS / HOUR_IN_MILLIS);
+ (int) (25.0 * DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS / HOUR_IN_MILLIS);
private static final int DEFAULT_MAX_JOB_COUNT_RARE = // 48/window = 2/hr = 16/session
(int) (2.0 * DEFAULT_WINDOW_SIZE_RARE_MS / HOUR_IN_MILLIS);
private static final int DEFAULT_MAX_JOB_COUNT_RESTRICTED = 10;
@@ -3268,24 +3180,24 @@ public final class QuotaController extends StateController {
// TODO(267949143): set a different limit for headless system apps
private static final long DEFAULT_EJ_LIMIT_EXEMPTED_MS = 60 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_LIMIT_ACTIVE_MS = 30 * MINUTE_IN_MILLIS;
- private static final long DEFAULT_EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_ACTIVE_MS;
+ private static final long DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_ACTIVE_MS;
+ private static final long DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS = 15 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_LIMIT_FREQUENT_MS = 10 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_LIMIT_RARE_MS = DEFAULT_EJ_LIMIT_FREQUENT_MS;
private static final long DEFAULT_EJ_LIMIT_RESTRICTED_MS = 5 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_LIMIT_ADDITION_SPECIAL_MS = 15 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_LIMIT_ADDITION_INSTALLER_MS = 30 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_WINDOW_SIZE_MS = 24 * HOUR_IN_MILLIS;
- private static final long DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS = 30 * SECOND_IN_MILLIS;
+ private static final long DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS =
+ 30 * SECOND_IN_MILLIS;
+ private static final long DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS =
+ 5 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_REWARD_TOP_APP_MS = 10 * SECOND_IN_MILLIS;
- private static final long DEFAULT_EJ_REWARD_INTERACTION_MS = 15 * SECOND_IN_MILLIS;
+ private static final long DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS = 15 * SECOND_IN_MILLIS;
+ private static final long DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS = 5 * SECOND_IN_MILLIS;
private static final long DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS = 0;
private static final long DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = 3 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS = 1 * MINUTE_IN_MILLIS;
- private static final long DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS = 1 * MINUTE_IN_MILLIS;
- private static final int DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT = 2;
- private static final int DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = 1;
- private static final long DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS = 8 * HOUR_IN_MILLIS;
- private static final int DEFAULT_QUOTA_BUMP_LIMIT = 8;
/**
* How much time each app in the exempted bucket will have to run jobs within their standby
@@ -3332,28 +3244,28 @@ public final class QuotaController extends StateController {
* expected to run only {@link #ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS} within the past
* WINDOW_SIZE_MS.
*/
- public long WINDOW_SIZE_EXEMPTED_MS = DEFAULT_WINDOW_SIZE_EXEMPTED_MS;
+ public long WINDOW_SIZE_EXEMPTED_MS = DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS;
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
* expected to run only {@link #ALLOWED_TIME_PER_PERIOD_ACTIVE_MS} within the past
* WINDOW_SIZE_MS.
*/
- public long WINDOW_SIZE_ACTIVE_MS = DEFAULT_WINDOW_SIZE_ACTIVE_MS;
+ public long WINDOW_SIZE_ACTIVE_MS = DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS;
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
* expected to run only {@link #ALLOWED_TIME_PER_PERIOD_WORKING_MS} within the past
* WINDOW_SIZE_MS.
*/
- public long WINDOW_SIZE_WORKING_MS = DEFAULT_WINDOW_SIZE_WORKING_MS;
+ public long WINDOW_SIZE_WORKING_MS = DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS;
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
* expected to run only {@link #ALLOWED_TIME_PER_PERIOD_FREQUENT_MS} within the past
* WINDOW_SIZE_MS.
*/
- public long WINDOW_SIZE_FREQUENT_MS = DEFAULT_WINDOW_SIZE_FREQUENT_MS;
+ public long WINDOW_SIZE_FREQUENT_MS = DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS;
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
@@ -3514,7 +3426,7 @@ public final class QuotaController extends StateController {
* standby bucket can only have expedited job sessions totalling EJ_LIMIT (without factoring
* in any rewards or free EJs).
*/
- public long EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_WORKING_MS;
+ public long EJ_LIMIT_WORKING_MS = DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS;
/**
* The total expedited job session limit of the particular standby bucket. Apps in this
@@ -3558,7 +3470,7 @@ public final class QuotaController extends StateController {
/**
* Length of time used to split an app's top time into chunks.
*/
- public long EJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
+ public long EJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
/**
* How much EJ quota to give back to an app based on the number of top app time chunks it
@@ -3569,7 +3481,7 @@ public final class QuotaController extends StateController {
/**
* How much EJ quota to give back to an app based on each non-top user interaction.
*/
- public long EJ_REWARD_INTERACTION_MS = DEFAULT_EJ_REWARD_INTERACTION_MS;
+ public long EJ_REWARD_INTERACTION_MS = DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS;
/**
* How much EJ quota to give back to an app based on each notification seen event.
@@ -3587,32 +3499,51 @@ public final class QuotaController extends StateController {
*/
public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
- /**
- * How much additional session duration to give an app for each accepted quota bump.
- */
- public long QUOTA_BUMP_ADDITIONAL_DURATION_MS = DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS;
+ void adjustDefaultBucketWindowSizes() {
+ WINDOW_SIZE_EXEMPTED_MS = DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
+ WINDOW_SIZE_ACTIVE_MS = DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
+ WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS;
+ WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS;
- /**
- * How many additional regular jobs to give an app for each accepted quota bump.
- */
- public int QUOTA_BUMP_ADDITIONAL_JOB_COUNT = DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT;
+ mBucketPeriodsMs[EXEMPTED_INDEX] = Math.max(
+ mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
+ Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS));
+ mBucketPeriodsMs[ACTIVE_INDEX] = Math.max(
+ mAllowedTimePerPeriodMs[ACTIVE_INDEX],
+ Math.min(MAX_PERIOD_MS, WINDOW_SIZE_ACTIVE_MS));
+ mBucketPeriodsMs[WORKING_INDEX] = Math.max(
+ mAllowedTimePerPeriodMs[WORKING_INDEX],
+ Math.min(MAX_PERIOD_MS, WINDOW_SIZE_WORKING_MS));
+ mBucketPeriodsMs[FREQUENT_INDEX] = Math.max(
+ mAllowedTimePerPeriodMs[FREQUENT_INDEX],
+ Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
+ }
- /**
- * How many additional sessions to give an app for each accepted quota bump.
- */
- public int QUOTA_BUMP_ADDITIONAL_SESSION_COUNT =
- DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT;
+ void adjustDefaultEjLimits() {
+ EJ_LIMIT_WORKING_MS = DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS;
+ EJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
+ EJ_REWARD_INTERACTION_MS = DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS;
- /**
- * The rolling window size within which to accept and apply quota bump events.
- */
- public long QUOTA_BUMP_WINDOW_SIZE_MS = DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS;
+ // The limit must be in the range [15 minutes, active limit].
+ mEJLimitsMs[WORKING_INDEX] = Math.max(15 * MINUTE_IN_MILLIS,
+ Math.min(mEJLimitsMs[ACTIVE_INDEX], EJ_LIMIT_WORKING_MS));
- /**
- * The maximum number of quota bumps to accept and apply within the
- * {@link #QUOTA_BUMP_WINDOW_SIZE_MS window}.
- */
- public int QUOTA_BUMP_LIMIT = DEFAULT_QUOTA_BUMP_LIMIT;
+ // Limit interaction reward to be in the range [5 seconds, 15 minutes] per event.
+ mEJRewardInteractionMs = Math.min(15 * MINUTE_IN_MILLIS,
+ Math.max(5 * SECOND_IN_MILLIS, EJ_REWARD_INTERACTION_MS));
+
+ // Limit chunking to be in the range [1 millisecond, 15 minutes] per event.
+ long newChunkSizeMs = Math.min(15 * MINUTE_IN_MILLIS,
+ Math.max(1, EJ_TOP_APP_TIME_CHUNK_SIZE_MS));
+ mEJTopAppTimeChunkSizeMs = newChunkSizeMs;
+ if (mEJTopAppTimeChunkSizeMs < mEJRewardTopAppMs) {
+ // Not making chunk sizes and top rewards to be the upper/lower
+ // limits of the other to allow trying different policies. Just log
+ // the discrepancy.
+ Slog.w(TAG, "EJ top app time chunk less than reward: "
+ + mEJTopAppTimeChunkSizeMs + " vs " + mEJRewardTopAppMs);
+ }
+ }
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
@@ -3650,14 +3581,6 @@ public final class QuotaController extends StateController {
updateEJLimitConstantsLocked();
break;
- case KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS:
- case KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT:
- case KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT:
- case KEY_QUOTA_BUMP_WINDOW_SIZE_MS:
- case KEY_QUOTA_BUMP_LIMIT:
- updateQuotaBumpConstantsLocked();
- break;
-
case KEY_MAX_JOB_COUNT_EXEMPTED:
MAX_JOB_COUNT_EXEMPTED = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_EXEMPTED);
int newExemptedMaxJobCount =
@@ -3790,7 +3713,9 @@ public final class QuotaController extends StateController {
case KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS:
// We don't need to re-evaluate execution stats or constraint status for this.
EJ_TOP_APP_TIME_CHUNK_SIZE_MS =
- properties.getLong(key, DEFAULT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS);
+ properties.getLong(key, Flags.adjustQuotaDefaultConstants()
+ ? DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS :
+ DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS);
// Limit chunking to be in the range [1 millisecond, 15 minutes] per event.
long newChunkSizeMs = Math.min(15 * MINUTE_IN_MILLIS,
Math.max(1, EJ_TOP_APP_TIME_CHUNK_SIZE_MS));
@@ -3826,7 +3751,9 @@ public final class QuotaController extends StateController {
case KEY_EJ_REWARD_INTERACTION_MS:
// We don't need to re-evaluate execution stats or constraint status for this.
EJ_REWARD_INTERACTION_MS =
- properties.getLong(key, DEFAULT_EJ_REWARD_INTERACTION_MS);
+ properties.getLong(key, Flags.adjustQuotaDefaultConstants()
+ ? DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS :
+ DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS);
// Limit interaction reward to be in the range [5 seconds, 15 minutes] per
// event.
mEJRewardInteractionMs = Math.min(15 * MINUTE_IN_MILLIS,
@@ -3900,14 +3827,23 @@ public final class QuotaController extends StateController {
MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
DEFAULT_MAX_EXECUTION_TIME_MS);
WINDOW_SIZE_EXEMPTED_MS = properties.getLong(KEY_WINDOW_SIZE_EXEMPTED_MS,
- DEFAULT_WINDOW_SIZE_EXEMPTED_MS);
+ Flags.adjustQuotaDefaultConstants()
+ ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
+ DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS);
WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
- DEFAULT_WINDOW_SIZE_ACTIVE_MS);
+ Flags.adjustQuotaDefaultConstants()
+ ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
+ DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS);
WINDOW_SIZE_WORKING_MS =
- properties.getLong(KEY_WINDOW_SIZE_WORKING_MS, DEFAULT_WINDOW_SIZE_WORKING_MS);
+ properties.getLong(KEY_WINDOW_SIZE_WORKING_MS,
+ Flags.adjustQuotaDefaultConstants()
+ ? DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS :
+ DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS);
WINDOW_SIZE_FREQUENT_MS =
properties.getLong(KEY_WINDOW_SIZE_FREQUENT_MS,
- DEFAULT_WINDOW_SIZE_FREQUENT_MS);
+ Flags.adjustQuotaDefaultConstants()
+ ? DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS :
+ DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS);
WINDOW_SIZE_RARE_MS = properties.getLong(KEY_WINDOW_SIZE_RARE_MS,
DEFAULT_WINDOW_SIZE_RARE_MS);
WINDOW_SIZE_RESTRICTED_MS =
@@ -4078,7 +4014,9 @@ public final class QuotaController extends StateController {
EJ_LIMIT_ACTIVE_MS = properties.getLong(
KEY_EJ_LIMIT_ACTIVE_MS, DEFAULT_EJ_LIMIT_ACTIVE_MS);
EJ_LIMIT_WORKING_MS = properties.getLong(
- KEY_EJ_LIMIT_WORKING_MS, DEFAULT_EJ_LIMIT_WORKING_MS);
+ KEY_EJ_LIMIT_WORKING_MS, Flags.adjustQuotaDefaultConstants()
+ ? DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS :
+ DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS);
EJ_LIMIT_FREQUENT_MS = properties.getLong(
KEY_EJ_LIMIT_FREQUENT_MS, DEFAULT_EJ_LIMIT_FREQUENT_MS);
EJ_LIMIT_RARE_MS = properties.getLong(
@@ -4156,65 +4094,6 @@ public final class QuotaController extends StateController {
}
}
- private void updateQuotaBumpConstantsLocked() {
- if (mQuotaBumpConstantsUpdated) {
- return;
- }
- mQuotaBumpConstantsUpdated = true;
-
- // Query the values as an atomic set.
- final DeviceConfig.Properties properties = DeviceConfig.getProperties(
- DeviceConfig.NAMESPACE_JOB_SCHEDULER,
- KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS,
- KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT,
- KEY_QUOTA_BUMP_WINDOW_SIZE_MS, KEY_QUOTA_BUMP_LIMIT);
- QUOTA_BUMP_ADDITIONAL_DURATION_MS = properties.getLong(
- KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS,
- DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS);
- QUOTA_BUMP_ADDITIONAL_JOB_COUNT = properties.getInt(
- KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT);
- QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = properties.getInt(
- KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT,
- DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT);
- QUOTA_BUMP_WINDOW_SIZE_MS = properties.getLong(
- KEY_QUOTA_BUMP_WINDOW_SIZE_MS, DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS);
- QUOTA_BUMP_LIMIT = properties.getInt(
- KEY_QUOTA_BUMP_LIMIT, DEFAULT_QUOTA_BUMP_LIMIT);
-
- // The window must be in the range [1 hour, 24 hours].
- long newWindowSizeMs = Math.max(HOUR_IN_MILLIS,
- Math.min(MAX_PERIOD_MS, QUOTA_BUMP_WINDOW_SIZE_MS));
- if (mQuotaBumpWindowSizeMs != newWindowSizeMs) {
- mQuotaBumpWindowSizeMs = newWindowSizeMs;
- mShouldReevaluateConstraints = true;
- }
- // The limit must be nonnegative.
- int newLimit = Math.max(0, QUOTA_BUMP_LIMIT);
- if (mQuotaBumpLimit != newLimit) {
- mQuotaBumpLimit = newLimit;
- mShouldReevaluateConstraints = true;
- }
- // The job count must be nonnegative.
- int newJobAddition = Math.max(0, QUOTA_BUMP_ADDITIONAL_JOB_COUNT);
- if (mQuotaBumpAdditionalJobCount != newJobAddition) {
- mQuotaBumpAdditionalJobCount = newJobAddition;
- mShouldReevaluateConstraints = true;
- }
- // The session count must be nonnegative.
- int newSessionAddition = Math.max(0, QUOTA_BUMP_ADDITIONAL_SESSION_COUNT);
- if (mQuotaBumpAdditionalSessionCount != newSessionAddition) {
- mQuotaBumpAdditionalSessionCount = newSessionAddition;
- mShouldReevaluateConstraints = true;
- }
- // The additional duration must be in the range [0, 10 minutes].
- long newAdditionalDuration = Math.max(0,
- Math.min(10 * MINUTE_IN_MILLIS, QUOTA_BUMP_ADDITIONAL_DURATION_MS));
- if (mQuotaBumpAdditionalDurationMs != newAdditionalDuration) {
- mQuotaBumpAdditionalDurationMs = newAdditionalDuration;
- mShouldReevaluateConstraints = true;
- }
- }
-
private void dump(IndentingPrintWriter pw) {
pw.println();
pw.println("QuotaController:");
@@ -4277,15 +4156,6 @@ public final class QuotaController extends StateController {
EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS).println();
pw.print(KEY_EJ_GRACE_PERIOD_TOP_APP_MS, EJ_GRACE_PERIOD_TOP_APP_MS).println();
- pw.print(KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS,
- QUOTA_BUMP_ADDITIONAL_DURATION_MS).println();
- pw.print(KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT,
- QUOTA_BUMP_ADDITIONAL_JOB_COUNT).println();
- pw.print(KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT,
- QUOTA_BUMP_ADDITIONAL_SESSION_COUNT).println();
- pw.print(KEY_QUOTA_BUMP_WINDOW_SIZE_MS, QUOTA_BUMP_WINDOW_SIZE_MS).println();
- pw.print(KEY_QUOTA_BUMP_LIMIT, QUOTA_BUMP_LIMIT).println();
-
pw.decreaseIndent();
}
@@ -4503,37 +4373,19 @@ public final class QuotaController extends StateController {
return mQcConstants;
}
- @VisibleForTesting
- long getQuotaBumpAdditionDurationMs() {
- return mQuotaBumpAdditionalDurationMs;
- }
-
- @VisibleForTesting
- int getQuotaBumpAdditionJobCount() {
- return mQuotaBumpAdditionalJobCount;
- }
-
- @VisibleForTesting
- int getQuotaBumpAdditionSessionCount() {
- return mQuotaBumpAdditionalSessionCount;
- }
-
- @VisibleForTesting
- int getQuotaBumpLimit() {
- return mQuotaBumpLimit;
- }
-
- @VisibleForTesting
- long getQuotaBumpWindowSizeMs() {
- return mQuotaBumpWindowSizeMs;
- }
-
//////////////////////////// DATA DUMP //////////////////////////////
@NeverCompile // Avoid size overhead of debugging code.
@Override
public void dumpControllerStateLocked(final IndentingPrintWriter pw,
final Predicate<JobStatus> predicate) {
+ pw.println("Flags: ");
+ pw.println(" " + Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS
+ + ": " + Flags.adjustQuotaDefaultConstants());
+ pw.println(" " + Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS
+ + ": " + Flags.enforceQuotaPolicyToFgsJobs());
+ pw.println();
+
pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis());
pw.println();
diff --git a/core/api/current.txt b/core/api/current.txt
index 035f82388e6e..14a91c9033c5 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8781,7 +8781,6 @@ package android.app.admin {
package android.app.appfunctions {
@FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionManager {
- method @Deprecated @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
method public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>);
@@ -8793,9 +8792,7 @@ package android.app.appfunctions {
@FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public abstract class AppFunctionService extends android.app.Service {
ctor public AppFunctionService();
method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
- method @Deprecated @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
- method @Deprecated @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
- method @MainThread public void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
+ method @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
}
@@ -32880,6 +32877,41 @@ package android.os {
}
@FlaggedApi("android.sdk.major_minor_versioning_scheme") public static class Build.VERSION_CODES_FULL {
+ field public static final int BASE = 100000; // 0x186a0
+ field public static final int BASE_1_1 = 200000; // 0x30d40
+ field public static final int CUPCAKE = 300000; // 0x493e0
+ field public static final int DONUT = 400000; // 0x61a80
+ field public static final int ECLAIR = 500000; // 0x7a120
+ field public static final int ECLAIR_0_1 = 600000; // 0x927c0
+ field public static final int ECLAIR_MR1 = 700000; // 0xaae60
+ field public static final int FROYO = 800000; // 0xc3500
+ field public static final int GINGERBREAD = 900000; // 0xdbba0
+ field public static final int GINGERBREAD_MR1 = 1000000; // 0xf4240
+ field public static final int HONEYCOMB = 1100000; // 0x10c8e0
+ field public static final int HONEYCOMB_MR1 = 1200000; // 0x124f80
+ field public static final int HONEYCOMB_MR2 = 1300000; // 0x13d620
+ field public static final int ICE_CREAM_SANDWICH = 1400000; // 0x155cc0
+ field public static final int ICE_CREAM_SANDWICH_MR1 = 1500000; // 0x16e360
+ field public static final int JELLY_BEAN = 1600000; // 0x186a00
+ field public static final int JELLY_BEAN_MR1 = 1700000; // 0x19f0a0
+ field public static final int JELLY_BEAN_MR2 = 1800000; // 0x1b7740
+ field public static final int KITKAT = 1900000; // 0x1cfde0
+ field public static final int KITKAT_WATCH = 2000000; // 0x1e8480
+ field public static final int LOLLIPOP = 2100000; // 0x200b20
+ field public static final int LOLLIPOP_MR1 = 2200000; // 0x2191c0
+ field public static final int M = 2300000; // 0x231860
+ field public static final int N = 2400000; // 0x249f00
+ field public static final int N_MR1 = 2500000; // 0x2625a0
+ field public static final int O = 2600000; // 0x27ac40
+ field public static final int O_MR1 = 2700000; // 0x2932e0
+ field public static final int P = 2800000; // 0x2ab980
+ field public static final int Q = 2900000; // 0x2c4020
+ field public static final int R = 3000000; // 0x2dc6c0
+ field public static final int S = 3100000; // 0x2f4d60
+ field public static final int S_V2 = 3200000; // 0x30d400
+ field public static final int TIRAMISU = 3300000; // 0x325aa0
+ field public static final int UPSIDE_DOWN_CAKE = 3400000; // 0x33e140
+ field public static final int VANILLA_ICE_CREAM = 3500000; // 0x3567e0
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -33933,12 +33965,9 @@ package android.os {
public class RemoteCallbackList<E extends android.os.IInterface> {
ctor public RemoteCallbackList();
method public int beginBroadcast();
- method @FlaggedApi("android.os.binder_frozen_state_change_callback") public void broadcast(@NonNull java.util.function.Consumer<E>);
method public void finishBroadcast();
method public Object getBroadcastCookie(int);
method public E getBroadcastItem(int);
- method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getFrozenCalleePolicy();
- method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getMaxQueueSize();
method public Object getRegisteredCallbackCookie(int);
method public int getRegisteredCallbackCount();
method public E getRegisteredCallbackItem(int);
@@ -33948,16 +33977,6 @@ package android.os {
method public boolean register(E);
method public boolean register(E, Object);
method public boolean unregister(E);
- field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_DROP = 3; // 0x3
- field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_ENQUEUE_ALL = 1; // 0x1
- field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT = 2; // 0x2
- field @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final int FROZEN_CALLEE_POLICY_UNSET = 0; // 0x0
- }
-
- @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final class RemoteCallbackList.Builder<E extends android.os.IInterface> {
- ctor public RemoteCallbackList.Builder(int);
- method @NonNull public android.os.RemoteCallbackList<E> build();
- method @NonNull public android.os.RemoteCallbackList.Builder setMaxQueueSize(int);
}
public class RemoteException extends android.util.AndroidException {
@@ -46367,7 +46386,7 @@ package android.telephony {
field public static final String ACTION_MULTI_SIM_CONFIG_CHANGED = "android.telephony.action.MULTI_SIM_CONFIG_CHANGED";
field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED";
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
- field @FlaggedApi("com.android.internal.telephony.flags.reset_mobile_network_settings") public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
+ field public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
field public static final String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
field public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE";
field public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
@@ -56601,7 +56620,7 @@ package android.view.inputmethod {
method public java.util.Map<android.view.inputmethod.InputMethodInfo,java.util.List<android.view.inputmethod.InputMethodSubtype>> getShortcutInputMethodsAndSubtypes();
method @Deprecated public void hideSoftInputFromInputMethod(android.os.IBinder, int);
method public boolean hideSoftInputFromWindow(android.os.IBinder, int);
- method public boolean hideSoftInputFromWindow(android.os.IBinder, int, android.os.ResultReceiver);
+ method @Deprecated public boolean hideSoftInputFromWindow(android.os.IBinder, int, android.os.ResultReceiver);
method @Deprecated public void hideStatusIcon(android.os.IBinder);
method public void invalidateInput(@NonNull android.view.View);
method public boolean isAcceptingText();
@@ -56625,7 +56644,7 @@ package android.view.inputmethod {
method public void showInputMethodAndSubtypeEnabler(@Nullable String);
method public void showInputMethodPicker();
method public boolean showSoftInput(android.view.View, int);
- method public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver);
+ method @Deprecated public boolean showSoftInput(android.view.View, int, android.os.ResultReceiver);
method @Deprecated public void showSoftInputFromInputMethod(android.os.IBinder, int);
method @Deprecated public void showStatusIcon(android.os.IBinder, String, @DrawableRes int);
method @FlaggedApi("android.view.inputmethod.connectionless_handwriting") public void startConnectionlessStylusHandwriting(@NonNull android.view.View, @Nullable android.view.inputmethod.CursorAnchorInfo, @NonNull java.util.concurrent.Executor, @NonNull android.view.inputmethod.ConnectionlessHandwritingCallback);
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 3c7c0d6e6ea1..a3cab291a1de 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -82,12 +82,12 @@ package android.database {
package android.graphics {
@Deprecated public class AvoidXfermode extends android.graphics.Xfermode {
- ctor public AvoidXfermode(int, int, android.graphics.AvoidXfermode.Mode);
+ ctor @Deprecated public AvoidXfermode(int, int, android.graphics.AvoidXfermode.Mode);
}
- public enum AvoidXfermode.Mode {
- enum_constant public static final android.graphics.AvoidXfermode.Mode AVOID;
- enum_constant public static final android.graphics.AvoidXfermode.Mode TARGET;
+ @Deprecated public enum AvoidXfermode.Mode {
+ enum_constant @Deprecated public static final android.graphics.AvoidXfermode.Mode AVOID;
+ enum_constant @Deprecated public static final android.graphics.AvoidXfermode.Mode TARGET;
}
public class Canvas {
@@ -102,9 +102,9 @@ package android.graphics {
}
@Deprecated public class LayerRasterizer extends android.graphics.Rasterizer {
- ctor public LayerRasterizer();
- method public void addLayer(android.graphics.Paint, float, float);
- method public void addLayer(android.graphics.Paint);
+ ctor @Deprecated public LayerRasterizer();
+ method @Deprecated public void addLayer(android.graphics.Paint, float, float);
+ method @Deprecated public void addLayer(android.graphics.Paint);
}
public class Paint {
@@ -118,7 +118,7 @@ package android.graphics {
}
@Deprecated public class PixelXorXfermode extends android.graphics.Xfermode {
- ctor public PixelXorXfermode(int);
+ ctor @Deprecated public PixelXorXfermode(int);
}
public class Rasterizer {
@@ -170,14 +170,14 @@ package android.media.tv {
package android.net {
@Deprecated public class NetworkBadging {
- method @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme);
- field public static final int BADGING_4K = 30; // 0x1e
- field public static final int BADGING_HD = 20; // 0x14
- field public static final int BADGING_NONE = 0; // 0x0
- field public static final int BADGING_SD = 10; // 0xa
+ method @Deprecated @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme);
+ field @Deprecated public static final int BADGING_4K = 30; // 0x1e
+ field @Deprecated public static final int BADGING_HD = 20; // 0x14
+ field @Deprecated public static final int BADGING_NONE = 0; // 0x0
+ field @Deprecated public static final int BADGING_SD = 10; // 0xa
}
- @IntDef({0x0, 0xa, 0x14, 0x1e}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NetworkBadging.Badging {
+ @Deprecated @IntDef({0x0, 0xa, 0x14, 0x1e}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NetworkBadging.Badging {
}
public final class Proxy {
@@ -304,14 +304,14 @@ package android.provider {
@Deprecated public static final class ContactsContract.RawContacts.StreamItems implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemsColumns {
field @Deprecated public static final String CONTENT_DIRECTORY = "stream_items";
- field public static final String _COUNT = "_count";
- field public static final String _ID = "_id";
+ field @Deprecated public static final String _COUNT = "_count";
+ field @Deprecated public static final String _ID = "_id";
}
@Deprecated public static final class ContactsContract.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns {
field @Deprecated public static final String PHOTO = "photo";
- field public static final String _COUNT = "_count";
- field public static final String _ID = "_id";
+ field @Deprecated public static final String _COUNT = "_count";
+ field @Deprecated public static final String _ID = "_id";
}
@Deprecated protected static interface ContactsContract.StreamItemPhotosColumns {
@@ -332,16 +332,16 @@ package android.provider {
field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item";
field @Deprecated public static final android.net.Uri CONTENT_URI;
field @Deprecated public static final String MAX_ITEMS = "max_items";
- field public static final String _COUNT = "_count";
- field public static final String _ID = "_id";
+ field @Deprecated public static final String _COUNT = "_count";
+ field @Deprecated public static final String _ID = "_id";
}
@Deprecated public static final class ContactsContract.StreamItems.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns {
field @Deprecated public static final String CONTENT_DIRECTORY = "photo";
field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/stream_item_photo";
field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item_photo";
- field public static final String _COUNT = "_count";
- field public static final String _ID = "_id";
+ field @Deprecated public static final String _COUNT = "_count";
+ field @Deprecated public static final String _ID = "_id";
}
@Deprecated protected static interface ContactsContract.StreamItemsColumns {
@@ -447,14 +447,14 @@ package android.text.style {
package android.util {
@Deprecated public class FloatMath {
- method public static float ceil(float);
- method public static float cos(float);
- method public static float exp(float);
- method public static float floor(float);
- method public static float hypot(float, float);
- method public static float pow(float, float);
- method public static float sin(float);
- method public static float sqrt(float);
+ method @Deprecated public static float ceil(float);
+ method @Deprecated public static float cos(float);
+ method @Deprecated public static float exp(float);
+ method @Deprecated public static float floor(float);
+ method @Deprecated public static float hypot(float, float);
+ method @Deprecated public static float pow(float, float);
+ method @Deprecated public static float sin(float);
+ method @Deprecated public static float sqrt(float);
}
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fa4fc43c3418..349d06ca8ad6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4170,9 +4170,11 @@ package android.content.pm {
}
public class PackageInstaller {
+ method @FlaggedApi("android.content.pm.verification_service") @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public final int getVerificationPolicy();
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @FlaggedApi("android.content.pm.read_install_info") @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
+ method @FlaggedApi("android.content.pm.verification_service") @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public final boolean setVerificationPolicy(int);
field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL";
field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2
@@ -4183,12 +4185,20 @@ package android.content.pm {
field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_DELETE_FLAGS = "android.content.pm.extra.DELETE_FLAGS";
field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
field @Deprecated public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
+ field @FlaggedApi("android.content.pm.verification_service") public static final String EXTRA_VERIFICATION_FAILURE_REASON = "android.content.pm.extra.VERIFICATION_FAILURE_REASON";
field public static final int LOCATION_DATA_APP = 0; // 0x0
field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
field public static final int REASON_CONFIRM_PACKAGE_CHANGE = 0; // 0x0
field public static final int REASON_OWNERSHIP_CHANGED = 1; // 0x1
field public static final int REASON_REMIND_OWNERSHIP = 2; // 0x2
+ field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE = 1; // 0x1
+ field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED = 2; // 0x2
+ field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_FAILED_REASON_UNKNOWN = 0; // 0x0
+ field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_BLOCK_FAIL_CLOSED = 3; // 0x3
+ field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_BLOCK_FAIL_OPEN = 1; // 0x1
+ field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_BLOCK_FAIL_WARN = 2; // 0x2
+ field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_NONE = 0; // 0x0
}
public static class PackageInstaller.InstallInfo {
@@ -4635,12 +4645,13 @@ package android.content.pm.verify.pkg {
method @NonNull public android.content.pm.SigningInfo getSigningInfo();
method @NonNull public android.net.Uri getStagedPackageUri();
method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long getTimeoutTime();
+ method public int getVerificationPolicy();
method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus);
method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle);
method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationIncomplete(int);
+ method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public boolean setVerificationPolicy(int);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.pkg.VerificationSession> CREATOR;
- field public static final int VERIFICATION_INCOMPLETE_NETWORK_LIMITED = 2; // 0x2
field public static final int VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1; // 0x1
field public static final int VERIFICATION_INCOMPLETE_UNKNOWN = 0; // 0x0
}
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index bbfa0ec3f3c2..78b9994e8fa1 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -7,8 +7,8 @@ package android.app {
}
@Deprecated public abstract static class AppOpsManager.AppOpsCollector extends android.app.AppOpsManager.OnOpNotedCallback {
- ctor public AppOpsManager.AppOpsCollector();
- method @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor();
+ ctor @Deprecated public AppOpsManager.AppOpsCollector();
+ method @Deprecated @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor();
}
public class Notification implements android.os.Parcelable {
@@ -207,7 +207,7 @@ package android.service.translation {
@Deprecated public static interface TranslationService.OnTranslationResultCallback {
method @Deprecated public void onError();
- method public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse);
+ method @Deprecated public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse);
}
}
@@ -261,64 +261,64 @@ package android.telephony.ims {
}
@Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable {
- method public boolean containsKey(@NonNull String);
- method @NonNull public android.os.PersistableBundle copyBundle();
- method public int describeContents();
- method public boolean getBoolean(@NonNull String, boolean);
- method public int getInt(@NonNull String, int);
- method @Nullable public String getString(@NonNull String);
- method public long getVersion();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
- field public static final String IPTYPE_IPV4 = "IPV4";
- field public static final String IPTYPE_IPV6 = "IPV6";
- field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
- field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
- field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
- field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
- field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
- field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
- field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
- field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
- field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
- field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
- field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
- field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
- field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
- field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
- field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
- field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
- field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
- field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
- field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
- field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
- field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
- field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
- field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
- field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
- field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
- field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
- field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
- field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
- field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
- field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
- field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
- field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
- field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
- field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
- field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
- field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
- field public static final String SIP_TRANSPORT_TCP = "TCP";
- field public static final String SIP_TRANSPORT_UDP = "UDP";
- }
-
- public static final class SipDelegateImsConfiguration.Builder {
- ctor public SipDelegateImsConfiguration.Builder(int);
- ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
- method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
- method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
- method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
- method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
+ method @Deprecated public boolean containsKey(@NonNull String);
+ method @Deprecated @NonNull public android.os.PersistableBundle copyBundle();
+ method @Deprecated public int describeContents();
+ method @Deprecated public boolean getBoolean(@NonNull String, boolean);
+ method @Deprecated public int getInt(@NonNull String, int);
+ method @Deprecated @Nullable public String getString(@NonNull String);
+ method @Deprecated public long getVersion();
+ method @Deprecated public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
+ field @Deprecated public static final String IPTYPE_IPV4 = "IPV4";
+ field @Deprecated public static final String IPTYPE_IPV6 = "IPV6";
+ field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
+ field @Deprecated public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
+ field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
+ field @Deprecated public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
+ field @Deprecated public static final String SIP_TRANSPORT_TCP = "TCP";
+ field @Deprecated public static final String SIP_TRANSPORT_UDP = "UDP";
+ }
+
+ @Deprecated public static final class SipDelegateImsConfiguration.Builder {
+ ctor @Deprecated public SipDelegateImsConfiguration.Builder(int);
+ ctor @Deprecated public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+ method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
+ method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
+ method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
+ method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
}
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index c79fc510c25a..1ff8c510b6bf 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1101,6 +1101,7 @@ package android.content.pm {
field public static final long OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT = 265452344L; // 0xfd27b38L
field public static final long OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION = 255940284L; // 0xf4156bcL
field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2
+ field public static final long UNIVERSAL_RESIZABLE_BY_DEFAULT = 357141415L; // 0x15498ba7L
}
public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 1265de1ebb15..d12e1bf77e17 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -19,6 +19,7 @@ filegroup {
srcs: [
"**/*.java",
"**/*.aidl",
+ ":systemfeatures-gen-srcs",
":framework-nfc-non-updatable-sources",
":messagequeue-gen",
":ranging_stack_mock_initializer",
@@ -214,6 +215,9 @@ filegroup {
aidl_interface {
name: "android.os.hintmanager_aidl",
+ defaults: [
+ "android.hardware.power-aidl",
+ ],
srcs: [
"android/os/IHintManager.aidl",
"android/os/IHintSession.aidl",
@@ -231,9 +235,6 @@ aidl_interface {
enabled: true,
},
},
- imports: [
- "android.hardware.power-V5",
- ],
}
aidl_library {
@@ -665,3 +666,29 @@ java_library {
}
// protolog end
+
+// Whether to enable read-only system feature codegen.
+gen_readonly_feature_apis = select(release_flag("RELEASE_USE_SYSTEM_FEATURE_BUILD_FLAGS"), {
+ true: "true",
+ false: "false",
+ default: "false",
+})
+
+// Generates com.android.internal.pm.RoSystemFeatures, optionally compiling in
+// details about fixed system features defined by build flags. When disabled,
+// the APIs are simply passthrough stubs with no meaningful side effects.
+genrule {
+ name: "systemfeatures-gen-srcs",
+ cmd: "$(location systemfeatures-gen-tool) com.android.internal.pm.RoSystemFeatures " +
+ // --readonly=false (default) makes the codegen an effective no-op passthrough API.
+ " --readonly=" + gen_readonly_feature_apis +
+ // For now, only export "android.hardware.type.*" system features APIs.
+ // TODO(b/203143243): Use an intermediate soong var that aggregates all declared
+ // RELEASE_SYSTEM_FEATURE_* declarations into a single arg.
+ " --feature-apis=AUTOMOTIVE,WATCH,TELEVISION,EMBEDDED,PC" +
+ " > $(out)",
+ out: [
+ "RoSystemFeatures.java",
+ ],
+ tools: ["systemfeatures-gen-tool"],
+}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7273e64846c0..36fc65a76d53 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1031,7 +1031,9 @@ public class ActivityManager {
| PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
/**
- * All implicit capabilities. There are capabilities that process automatically have.
+ * All implicit capabilities. This capability set is currently only used for processes under
+ * active instrumentation. The intent is to allow CTS tests to always have these capabilities
+ * so that every test doesn't need to launch FGS.
* @hide
*/
@TestApi
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 3bd121a4a19b..f80121d0c9b6 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -1328,7 +1328,8 @@ public abstract class ActivityManagerInternal {
* Add a creator token for all embedded intents (stored as extra) of the given intent.
*
* @param intent The given intent
+ * @param creatorPackage the package name of the creator app.
* @hide
*/
- public abstract void addCreatorToken(Intent intent);
+ public abstract void addCreatorToken(Intent intent, String creatorPackage);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 99625ac20e60..e7f4dbc24022 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -7346,6 +7346,8 @@ public final class ActivityThread extends ClientTransactionHandler
}
}
+ VMDebug.setUserId(UserHandle.myUserId());
+ VMDebug.addApplication(data.appInfo.packageName);
// send up app name; do this *before* waiting for debugger
Process.setArgV0(data.processName);
android.ddm.DdmHandleAppName.setAppName(data.processName,
@@ -7868,9 +7870,20 @@ public final class ActivityThread extends ClientTransactionHandler
file.getParentFile().mkdirs();
Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
}
+
+ if (ii.packageName != null) {
+ VMDebug.addApplication(ii.packageName);
+ }
}
private void handleFinishInstrumentationWithoutRestart() {
+ LoadedApk loadedApk = getApplication().mLoadedApk;
+ // Only remove instrumentation app if this was not a self-testing app.
+ if (mInstrumentationPackageName != null && loadedApk != null && !mInstrumentationPackageName
+ .equals(loadedApk.mPackageName)) {
+ VMDebug.removeApplication(mInstrumentationPackageName);
+ }
+
mInstrumentation.onDestroy();
mInstrumentationPackageName = null;
mInstrumentationAppDir = null;
@@ -8904,6 +8917,11 @@ public final class ActivityThread extends ClientTransactionHandler
return false;
}
+ void addApplication(@NonNull Application app) {
+ mAllApplications.add(app);
+ VMDebug.addApplication(app.mLoadedApk.mPackageName);
+ }
+
@Override
public boolean isInDensityCompatMode() {
return mDensityCompatMode;
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 1df8f63aa402..1e45d6fd1674 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1478,7 +1478,7 @@ public final class LoadedApk {
+ " package " + mPackageName + ": " + e.toString(), e);
}
}
- mActivityThread.mAllApplications.add(app);
+ mActivityThread.addApplication(app);
mApplication = app;
if (!allowDuplicateInstances) {
synchronized (sApplications) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 8b33417e0a79..4ef5b5163fef 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -11296,7 +11296,11 @@ public class Notification implements Parcelable
* @see Segment
*/
public @NonNull ProgressStyle setProgressSegments(@NonNull List<Segment> progressSegments) {
- mProgressSegments = new ArrayList<>(progressSegments.size());
+ if (mProgressSegments == null) {
+ mProgressSegments = new ArrayList<>();
+ }
+ mProgressSegments.clear();
+ mProgressSegments.addAll(progressSegments);
return this;
}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index d363e19bcc19..ba71afb49629 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -97,6 +97,7 @@ per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/serve
# Performance
per-file PropertyInvalidatedCache.java = file:/PERFORMANCE_OWNERS
+per-file performance.aconfig = file:/PERFORMANCE_OWNERS
# Pinner
per-file pinner-client.aconfig = file:/core/java/android/app/pinner/OWNERS
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index 439d988e2588..dca433696fe7 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -110,40 +110,6 @@ public final class AppFunctionManager {
*
* @param request the request to execute the app function
* @param executor the executor to run the callback
- * @param callback the callback to receive the function execution result. if the calling app
- * does not own the app function or does not have {@code
- * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
- * android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain {@code
- * ExecuteAppFunctionResponse.RESULT_DENIED}.
- * @deprecated Use {@link #executeAppFunction(ExecuteAppFunctionRequest, Executor,
- * CancellationSignal, Consumer)} instead. This method will be removed once usage references
- * are updated.
- */
- @RequiresPermission(
- anyOf = {
- Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
- Manifest.permission.EXECUTE_APP_FUNCTIONS
- },
- conditional = true)
- @UserHandleAware
- @Deprecated
- public void executeAppFunction(
- @NonNull ExecuteAppFunctionRequest request,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
- executeAppFunction(request, executor, new CancellationSignal(), callback);
- }
-
- /**
- * Executes the app function.
- *
- * <p>Note: Applications can execute functions they define. To execute functions defined in
- * another component, apps would need to have {@code
- * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code
- * android.permission.EXECUTE_APP_FUNCTIONS}.
- *
- * @param request the request to execute the app function
- * @param executor the executor to run the callback
* @param cancellationSignal the cancellation signal to cancel the execution.
* @param callback the callback to receive the function execution result. if the calling app
* does not own the app function or does not have {@code
diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
index 08ecced234a9..06d95f5270c3 100644
--- a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
+++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
@@ -137,8 +137,10 @@ public class AppFunctionRuntimeMetadata extends GenericDocument {
.TOKENIZER_TYPE_VERBATIM)
.build())
.addProperty(
- new AppSearchSchema.BooleanPropertyConfig.Builder(PROPERTY_ENABLED)
+ new AppSearchSchema.LongPropertyConfig.Builder(PROPERTY_ENABLED)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(
+ AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE)
.build())
.addProperty(
new AppSearchSchema.StringPropertyConfig.Builder(
@@ -212,19 +214,14 @@ public class AppFunctionRuntimeMetadata extends GenericDocument {
}
/**
- * Sets an indicator specifying if the function is enabled or not. This would override the
- * default enabled state in the static metadata ({@link
- * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT}). Sets this to null
- * to clear the override.
- * TODO(369683073) Replace the tristate Boolean with IntDef EnabledState.
+ * Sets an indicator specifying the function enabled state.
*/
@NonNull
public Builder setEnabled(@EnabledState int enabledState) {
if (enabledState != APP_FUNCTION_STATE_DEFAULT
&& enabledState != APP_FUNCTION_STATE_ENABLED
&& enabledState != APP_FUNCTION_STATE_DISABLED) {
- throw new IllegalArgumentException(
- "Value of EnabledState is unsupported.");
+ throw new IllegalArgumentException("Value of EnabledState is unsupported.");
}
setPropertyLong(PROPERTY_ENABLED, enabledState);
return this;
diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java
index ceca850a1037..63d187aa11ef 100644
--- a/core/java/android/app/appfunctions/AppFunctionService.java
+++ b/core/java/android/app/appfunctions/AppFunctionService.java
@@ -158,74 +158,6 @@ public abstract class AppFunctionService extends Service {
* thread and dispatch the result with the given callback. You should always report back the
* result using the callback, no matter if the execution was successful or not.
*
- * @param request The function execution request.
- * @param callback A callback to report back the result.
- * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, CancellationSignal,
- * Consumer)} instead. This method will be removed once usage references are updated.
- */
- @MainThread
- @Deprecated
- public void onExecuteFunction(
- @NonNull ExecuteAppFunctionRequest request,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
- Log.w(
- "AppFunctionService",
- "Calling deprecated default implementation of onExecuteFunction");
- }
-
- /**
- * Called by the system to execute a specific app function.
- *
- * <p>This method is triggered when the system requests your AppFunctionService to handle a
- * particular function you have registered and made available.
- *
- * <p>To ensure proper routing of function requests, assign a unique identifier to each
- * function. This identifier doesn't need to be globally unique, but it must be unique within
- * your app. For example, a function to order food could be identified as "orderFood". In most
- * cases this identifier should come from the ID automatically generated by the AppFunctions
- * SDK. You can determine the specific function to invoke by calling {@link
- * ExecuteAppFunctionRequest#getFunctionIdentifier()}.
- *
- * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker
- * thread and dispatch the result with the given callback. You should always report back the
- * result using the callback, no matter if the execution was successful or not.
- *
- * <p>This method also accepts a {@link CancellationSignal} that the app should listen to cancel
- * the execution of function if requested by the system.
- *
- * @param request The function execution request.
- * @param cancellationSignal A signal to cancel the execution.
- * @param callback A callback to report back the result.
- * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, String,
- * CancellationSignal, Consumer)} instead. This method will be removed once usage references
- * are updated.
- */
- @MainThread
- @Deprecated
- public void onExecuteFunction(
- @NonNull ExecuteAppFunctionRequest request,
- @NonNull CancellationSignal cancellationSignal,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
- onExecuteFunction(request, callback);
- }
-
- /**
- * Called by the system to execute a specific app function.
- *
- * <p>This method is triggered when the system requests your AppFunctionService to handle a
- * particular function you have registered and made available.
- *
- * <p>To ensure proper routing of function requests, assign a unique identifier to each
- * function. This identifier doesn't need to be globally unique, but it must be unique within
- * your app. For example, a function to order food could be identified as "orderFood". In most
- * cases this identifier should come from the ID automatically generated by the AppFunctions
- * SDK. You can determine the specific function to invoke by calling {@link
- * ExecuteAppFunctionRequest#getFunctionIdentifier()}.
- *
- * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker
- * thread and dispatch the result with the given callback. You should always report back the
- * result using the callback, no matter if the execution was successful or not.
- *
* <p>This method also accepts a {@link CancellationSignal} that the app should listen to cancel
* the execution of function if requested by the system.
*
@@ -235,11 +167,9 @@ public abstract class AppFunctionService extends Service {
* @param callback A callback to report back the result.
*/
@MainThread
- public void onExecuteFunction(
+ public abstract void onExecuteFunction(
@NonNull ExecuteAppFunctionRequest request,
@NonNull String callingPackage,
@NonNull CancellationSignal cancellationSignal,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
- onExecuteFunction(request, cancellationSignal, callback);
- }
+ @NonNull Consumer<ExecuteAppFunctionResponse> callback);
}
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 11e885055162..37fa9a26b91c 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -6,6 +6,14 @@ container: "system"
# flag relates to.
flag {
+ name: "notifications_redesign_app_icons"
+ namespace: "systemui"
+ description: "Notifications Redesign: Use app icons in notification rows (not to be confused with"
+ " notifications_use_app_icons, notifications_use_app_icon_in_row which are just experiments)."
+ bug: "371174789"
+}
+
+flag {
name: "modes_api"
is_exported: true
namespace: "systemui"
diff --git a/core/java/android/app/usage/UsageEventsQuery.java b/core/java/android/app/usage/UsageEventsQuery.java
index c0f13ca557e2..ecf4cd115fef 100644
--- a/core/java/android/app/usage/UsageEventsQuery.java
+++ b/core/java/android/app/usage/UsageEventsQuery.java
@@ -75,7 +75,7 @@ public final class UsageEventsQuery implements Parcelable {
}
/**
- * Returns the exclusive timpstamp to indicate the end of the range of events.
+ * Returns the exclusive timestamp to indicate the end of the range of events.
* Defined in terms of "Unix time", see {@link java.lang.System#currentTimeMillis}.
*/
public @CurrentTimeMillisLong long getEndTimeMillis() {
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 7f30d7cccb57..124973489dd1 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -287,7 +287,7 @@ public final class AssociationInfo implements Parcelable {
/**
* Get the device icon of the associated device. The device icon represents the device type.
*
- * @return the device icon, or {@code null} if no device icon is has been set for the
+ * @return the device icon, or {@code null} if no device icon has been set for the
* associated device.
*
* @see AssociationRequest.Builder#setDeviceIcon(Icon)
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 41a6791d8a7b..f368935a74c8 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -475,8 +475,8 @@ public final class AssociationRequest implements Parcelable {
}
/**
- * Set the device icon for the self-managed device and this icon will be
- * displayed in the self-managed association dialog.
+ * Set the device icon for the self-managed device and to display the icon in the
+ * self-managed association dialog.
*
* @throws IllegalArgumentException if the icon is not exactly 24dp by 24dp
* or if it is {@link Icon#TYPE_URI} or {@link Icon#TYPE_URI_ADAPTIVE_BITMAP}.
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index dfad6de4ba16..4472c3d13d7c 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -478,6 +478,15 @@ public final class CompanionDeviceManager {
Objects.requireNonNull(callback, "Callback cannot be null");
handler = Handler.mainIfNull(handler);
+ if (Flags.associationDeviceIcon()) {
+ final Icon deviceIcon = request.getDeviceIcon();
+
+ if (deviceIcon != null && !isValidIcon(deviceIcon, mContext)) {
+ throw new IllegalArgumentException("The size of the device icon must be "
+ + "24dp x 24dp to ensure proper display");
+ }
+ }
+
try {
mService.associate(request, new AssociationRequestCallbackProxy(handler, callback),
mContext.getOpPackageName(), mContext.getUserId());
@@ -542,11 +551,13 @@ public final class CompanionDeviceManager {
Objects.requireNonNull(executor, "Executor cannot be null");
Objects.requireNonNull(callback, "Callback cannot be null");
- final Icon deviceIcon = request.getDeviceIcon();
+ if (Flags.associationDeviceIcon()) {
+ final Icon deviceIcon = request.getDeviceIcon();
- if (deviceIcon != null && !isValidIcon(deviceIcon, mContext)) {
- throw new IllegalArgumentException("The size of the device icon must be 24dp x 24dp to"
- + "ensure proper display");
+ if (deviceIcon != null && !isValidIcon(deviceIcon, mContext)) {
+ throw new IllegalArgumentException("The size of the device icon must be "
+ + "24dp x 24dp to ensure proper display");
+ }
}
try {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 91f7a8bae163..628435da3a37 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5658,6 +5658,14 @@ public abstract class Context {
public static final String BINARY_TRANSPARENCY_SERVICE = "transparency";
/**
+ * System service name for ForensicService.
+ * The service manages the forensic info on device.
+ * @hide
+ */
+ @FlaggedApi(android.security.Flags.FLAG_AFL_API)
+ public static final String FORENSIC_SERVICE = "forensic";
+
+ /**
* System service name for the DeviceIdleManager.
* @see #getSystemService(String)
* @hide
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0bb0027fb0c3..f71952849872 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -888,6 +888,22 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_ACTIVITY_RECOGNIZER =
"android.intent.action.ACTIVITY_RECOGNIZER";
+ /** @hide */
+ public static void maybeMarkAsMissingCreatorToken(Object object) {
+ if (object instanceof Intent intent) {
+ maybeMarkAsMissingCreatorTokenInternal(intent);
+ }
+ }
+
+ private static void maybeMarkAsMissingCreatorTokenInternal(Intent intent) {
+ boolean isForeign = (intent.mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0;
+ boolean isWithoutTrustedCreatorToken =
+ (intent.mLocalFlags & Intent.LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT) == 0;
+ if (isForeign && isWithoutTrustedCreatorToken) {
+ intent.addExtendedFlags(EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN);
+ }
+ }
+
/**
* Represents a shortcut/live folder icon resource.
*
@@ -7684,10 +7700,8 @@ public class Intent implements Parcelable, Cloneable {
/**
* This flag indicates the creator token of this intent has been verified.
- *
- * @hide
*/
- public static final int LOCAL_FLAG_CREATOR_TOKEN_VERIFIED = 1 << 6;
+ private static final int LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT = 1 << 6;
/** @hide */
@IntDef(flag = true, prefix = { "EXTENDED_FLAG_" }, value = {
@@ -12243,6 +12257,30 @@ public class Intent implements Parcelable, Cloneable {
}
}
+ /** @hide */
+ public void checkCreatorToken() {
+ if (mExtras == null) return;
+ if (mCreatorTokenInfo != null && mCreatorTokenInfo.mExtraIntentKeys != null) {
+ for (String key : mCreatorTokenInfo.mExtraIntentKeys) {
+ try {
+ Intent extraIntent = mExtras.getParcelable(key, Intent.class);
+ if (extraIntent == null) {
+ Log.w(TAG, "The key {" + key
+ + "} does not correspond to an intent in the bundle.");
+ continue;
+ }
+ extraIntent.mLocalFlags |= LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to validate creator token. key: " + key + ".", e);
+ }
+ }
+ }
+ // mark the bundle as intent extras after calls to getParcelable.
+ // otherwise, the logic to mark missing token would run before
+ // mark trusted creator token present.
+ mExtras.setIsIntentExtra();
+ }
+
public void writeToParcel(Parcel out, int flags) {
out.writeString8(mAction);
Uri.writeToParcel(out, mData);
@@ -12730,6 +12768,7 @@ public class Intent implements Parcelable, Cloneable {
}
mLocalFlags |= localFlags;
+ checkCreatorToken();
// Special attribution fix-up logic for any BluetoothDevice extras
// passed via Bluetooth intents
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 481e6b530162..ce52825ddb73 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -21,10 +21,12 @@ import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.Activity;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -1204,6 +1206,18 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
public @interface SizeChangesSupportMode {}
/**
+ * This change id makes the restriction of fixed orientation, aspect ratio, and resizability
+ * of the app to be ignored, which means making the app fill the given available area.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @TestApi
+ @SuppressLint("UnflaggedApi") // @TestApi without associated public API.
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ public static final long UNIVERSAL_RESIZABLE_BY_DEFAULT = 357141415L; // buganizer id
+
+ /**
* This change id enables compat policy that ignores app requested orientation in
* response to an app calling {@link android.app.Activity#setRequestedOrientation}. See
* com.android.server.wm.LetterboxUiController#shouldIgnoreRequestedOrientation for
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index 451c0e5e079a..c911326ccffd 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -93,4 +93,10 @@ interface IPackageInstaller {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
void reportUnarchivalStatus(int unarchiveId, int status, long requiredStorageBytes, in PendingIntent userActionIntent, in UserHandle userHandle);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+ int getVerificationPolicy();
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+ boolean setVerificationPolicy(int policy);
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index c673d5846d5d..cba7bc912666 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -62,6 +62,8 @@ import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.pm.verify.domain.DomainSet;
+import android.content.pm.verify.pkg.VerificationSession;
+import android.content.pm.verify.pkg.VerificationStatus;
import android.graphics.Bitmap;
import android.icu.util.ULocale;
import android.net.Uri;
@@ -418,6 +420,21 @@ public class PackageInstaller {
public static final String EXTRA_WARNINGS = "android.content.pm.extra.WARNINGS";
/**
+ * When verification is blocked as part of the installation, additional reason for the block
+ * will be provided to the installer with a {@link VerificationFailedReason} as part of the
+ * installation result returned via the {@link IntentSender} in
+ * {@link Session#commit(IntentSender)}. This extra is provided only when the installation has
+ * failed. Installers can use this extra to check if the installation failure was caused by a
+ * verification failure.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+ @SystemApi
+ public static final String EXTRA_VERIFICATION_FAILURE_REASON =
+ "android.content.pm.extra.VERIFICATION_FAILURE_REASON";
+
+ /**
* Streaming installation pending.
* Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
*
@@ -760,6 +777,90 @@ public class PackageInstaller {
@Retention(RetentionPolicy.SOURCE)
public @interface UnarchivalStatus {}
+ /**
+ * Verification failed because of unknown reasons, such as when the verifier times out or cannot
+ * be connected. It can also corresponds to the status of
+ * {@link VerificationSession#VERIFICATION_INCOMPLETE_UNKNOWN} reported by the verifier via
+ * {@link VerificationSession#reportVerificationIncomplete(int)}.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+ @SystemApi
+ public static final int VERIFICATION_FAILED_REASON_UNKNOWN = 0;
+
+ /**
+ * Verification failed because the network is unavailable. This corresponds to the status of
+ * {@link VerificationSession#VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE} reported by the
+ * verifier via {@link VerificationSession#reportVerificationIncomplete(int)}.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+ @SystemApi
+ public static final int VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE = 1;
+
+ /**
+ * Verification failed because the package is blocked, as reported by the verifier via
+ * {@link VerificationSession#reportVerificationComplete(VerificationStatus)} or
+ * {@link VerificationSession#reportVerificationComplete(VerificationStatus, PersistableBundle)}
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+ @SystemApi
+ public static final int VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED = 2;
+
+ /**
+ * @hide
+ */
+ @IntDef(value = {
+ VERIFICATION_FAILED_REASON_UNKNOWN,
+ VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE,
+ VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED,
+ })
+ public @interface VerificationFailedReason {
+ }
+
+ /**
+ * Do not block installs, regardless of verification status.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+ @SystemApi
+ public static final int VERIFICATION_POLICY_NONE = 0; // platform default
+ /**
+ * Only block installations on {@link #VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED}.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+ @SystemApi
+ public static final int VERIFICATION_POLICY_BLOCK_FAIL_OPEN = 1;
+ /**
+ * Only block installations on {@link #VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED} and ask the
+ * user if they'd like to install anyway when the verification is blocked for other reason.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+ @SystemApi
+ public static final int VERIFICATION_POLICY_BLOCK_FAIL_WARN = 2;
+ /**
+ * Block installations whose verification status is blocked for any reason.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+ @SystemApi
+ public static final int VERIFICATION_POLICY_BLOCK_FAIL_CLOSED = 3;
+ /**
+ * @hide
+ */
+ @IntDef(value = {
+ VERIFICATION_POLICY_NONE,
+ VERIFICATION_POLICY_BLOCK_FAIL_OPEN,
+ VERIFICATION_POLICY_BLOCK_FAIL_WARN,
+ VERIFICATION_POLICY_BLOCK_FAIL_CLOSED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VerificationPolicy {
+ }
/** Default set of checksums - includes all available checksums.
* @see Session#requestChecksums */
@@ -1503,6 +1604,40 @@ public class PackageInstaller {
}
/**
+ * Return the current verification enforcement policy. This may only be called by the
+ * package currently set by the system as the verifier agent.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
+ public final @VerificationPolicy int getVerificationPolicy() {
+ try {
+ return mInstaller.getVerificationPolicy();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the current verification enforcement policy which will be applied to all the future
+ * installation sessions. This may only be called by the package currently set by the system as
+ * the verifier agent.
+ * @hide
+ * @return whether the new policy was successfully set.
+ */
+ @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
+ public final boolean setVerificationPolicy(@VerificationPolicy int policy) {
+ try {
+ return mInstaller.setVerificationPolicy(policy);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* An installation that is being actively staged. For an install to succeed,
* all existing and new packages must have identical package names, version
* codes, and signing certificates.
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index c7d7dc1eb0de..5b38942d468d 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -326,3 +326,19 @@ flag {
bug: "360129103"
is_fixed_read_only: true
}
+
+flag {
+ name: "include_feature_flags_in_package_cacher"
+ namespace: "package_manager_service"
+ description: "Include feature flag status when determining hits or misses in PackageCacher."
+ bug: "364771256"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "reduce_broadcasts_for_component_state_changes"
+ namespace: "package_manager_service"
+ description: "Feature flag to limit sending of the PACKAGE_CHANGED broadcast to only the system and the application itself during component state changes."
+ bug: "292261144"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 7de7131fc2ad..fa3bc9e16a6b 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -211,6 +211,18 @@ flag {
}
flag {
+ name: "property_invalidated_cache_bypass_mismatched_uids"
+ namespace: "multiuser"
+ description: "Bypass the cache when the process UID does not match the binder UID."
+ bug: "373752556"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ is_fixed_read_only: true
+}
+
+
+flag {
name: "cache_profile_parent_read_only"
namespace: "multiuser"
description: "Cache getProfileParent to avoid unnecessary binder calls"
diff --git a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
index 7a9484abd1b1..036c1e69cb0d 100644
--- a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
+++ b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
@@ -25,4 +25,6 @@ interface IVerificationSessionInterface {
long getTimeoutTime(int verificationId);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
long extendTimeRemaining(int verificationId, long additionalMs);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+ boolean setVerificationPolicy(int verificationId, int policy);
} \ No newline at end of file
diff --git a/core/java/android/content/pm/verify/pkg/VerificationSession.java b/core/java/android/content/pm/verify/pkg/VerificationSession.java
index 70b4a022f521..f393be829aed 100644
--- a/core/java/android/content/pm/verify/pkg/VerificationSession.java
+++ b/core/java/android/content/pm/verify/pkg/VerificationSession.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.pm.Flags;
+import android.content.pm.PackageInstaller;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningInfo;
import android.net.Uri;
@@ -52,17 +53,12 @@ public final class VerificationSession implements Parcelable {
* The verification cannot be completed because the network is unavailable.
*/
public static final int VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1;
- /**
- * The verification cannot be completed because the network is limited.
- */
- public static final int VERIFICATION_INCOMPLETE_NETWORK_LIMITED = 2;
/**
* @hide
*/
@IntDef(prefix = {"VERIFICATION_INCOMPLETE_"}, value = {
VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE,
- VERIFICATION_INCOMPLETE_NETWORK_LIMITED,
VERIFICATION_INCOMPLETE_UNKNOWN,
})
@Retention(RetentionPolicy.SOURCE)
@@ -85,6 +81,15 @@ public final class VerificationSession implements Parcelable {
private final IVerificationSessionInterface mSession;
@NonNull
private final IVerificationSessionCallback mCallback;
+ /**
+ * The current policy that is active for the session. It might not be
+ * the same as the original policy that was initially assigned for this verification session,
+ * because the active policy can be overridden by {@link #setVerificationPolicy(int)}.
+ * <p>To improve the latency, store the original policy value and any changes made to it,
+ * so that {@link #getVerificationPolicy()} does not need to make a binder call to retrieve the
+ * currently active policy.</p>
+ */
+ private volatile @PackageInstaller.VerificationPolicy int mVerificationPolicy;
/**
* Constructor used by the system to describe the details of a verification session.
@@ -94,6 +99,7 @@ public final class VerificationSession implements Parcelable {
@NonNull Uri stagedPackageUri, @NonNull SigningInfo signingInfo,
@NonNull List<SharedLibraryInfo> declaredLibraries,
@NonNull PersistableBundle extensionParams,
+ @PackageInstaller.VerificationPolicy int defaultPolicy,
@NonNull IVerificationSessionInterface session,
@NonNull IVerificationSessionCallback callback) {
mId = id;
@@ -103,6 +109,7 @@ public final class VerificationSession implements Parcelable {
mSigningInfo = signingInfo;
mDeclaredLibraries = declaredLibraries;
mExtensionParams = extensionParams;
+ mVerificationPolicy = defaultPolicy;
mSession = session;
mCallback = callback;
}
@@ -144,7 +151,7 @@ public final class VerificationSession implements Parcelable {
/**
* Returns a mapping of any shared libraries declared in the manifest
- * to the {@link SharedLibraryInfo#Type} that is declared. This will be an empty
+ * to the {@link SharedLibraryInfo.Type} that is declared. This will be an empty
* map if no shared libraries are declared by the package.
*/
@NonNull
@@ -174,6 +181,39 @@ public final class VerificationSession implements Parcelable {
}
/**
+ * Return the current policy that is active for this session.
+ * <p>If the policy for this session has been changed by {@link #setVerificationPolicy},
+ * the return value of this method is the current policy that is active for this session.
+ * Otherwise, the return value is the same as the initial policy that was assigned to the
+ * session when it was first created.</p>
+ */
+ public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
+ return mVerificationPolicy;
+ }
+
+ /**
+ * Override the verification policy for this session.
+ * @return True if the override was successful, False otherwise.
+ */
+ @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
+ public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
+ if (mVerificationPolicy == policy) {
+ // No effective policy change
+ return true;
+ }
+ try {
+ if (mSession.setVerificationPolicy(mId, policy)) {
+ mVerificationPolicy = policy;
+ return true;
+ } else {
+ return false;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Extend the timeout for this session by the provided additionalMs to
* fetch relevant information over the network or wait for the network.
* This may be called multiple times. If the request would bypass any max
@@ -239,6 +279,7 @@ public final class VerificationSession implements Parcelable {
mSigningInfo = SigningInfo.CREATOR.createFromParcel(in);
mDeclaredLibraries = in.createTypedArrayList(SharedLibraryInfo.CREATOR);
mExtensionParams = in.readPersistableBundle(getClass().getClassLoader());
+ mVerificationPolicy = in.readInt();
mSession = IVerificationSessionInterface.Stub.asInterface(in.readStrongBinder());
mCallback = IVerificationSessionCallback.Stub.asInterface(in.readStrongBinder());
}
@@ -257,6 +298,7 @@ public final class VerificationSession implements Parcelable {
mSigningInfo.writeToParcel(dest, flags);
dest.writeTypedList(mDeclaredLibraries);
dest.writePersistableBundle(mExtensionParams);
+ dest.writeInt(mVerificationPolicy);
dest.writeStrongBinder(mSession.asBinder());
dest.writeStrongBinder(mCallback.asBinder());
}
diff --git a/core/java/android/content/pm/xr.aconfig b/core/java/android/content/pm/xr.aconfig
new file mode 100644
index 000000000000..61835c162c49
--- /dev/null
+++ b/core/java/android/content/pm/xr.aconfig
@@ -0,0 +1,9 @@
+package: "android.xr"
+container: "system"
+
+flag {
+ namespace: "xr"
+ name: "xr_manifest_entries"
+ description: "Adds manifest entries used by Android XR"
+ bug: "364416355"
+} \ No newline at end of file
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 99eeca98bce0..f03a4a954842 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -142,3 +142,10 @@ flag {
description: "Controls whether the connected mice's primary buttons, left and right, can be swapped."
bug: "352598211"
}
+
+flag {
+ name: "keyboard_a11y_shortcut_control"
+ namespace: "input"
+ description: "Adds shortcuts to toggle and control a11y features"
+ bug: "373458181"
+}
diff --git a/core/java/android/hardware/radio/RadioAlert.aidl b/core/java/android/hardware/radio/RadioAlert.aidl
new file mode 100644
index 000000000000..17f4fc7e9a13
--- /dev/null
+++ b/core/java/android/hardware/radio/RadioAlert.aidl
@@ -0,0 +1,20 @@
+/**
+ * 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 android.hardware.radio;
+
+/** @hide */
+parcelable RadioAlert; \ No newline at end of file
diff --git a/core/java/android/hardware/radio/RadioAlert.java b/core/java/android/hardware/radio/RadioAlert.java
new file mode 100644
index 000000000000..b55dcd82ef7b
--- /dev/null
+++ b/core/java/android/hardware/radio/RadioAlert.java
@@ -0,0 +1,505 @@
+/**
+ * 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 android.hardware.radio;
+
+import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Emergency Alert Message
+ *
+ * <p>Alert message can be sent from a radio station of technologies such as HD radio to
+ * the radio users for some emergency events (see ITU-T X.1303 bis for more info).
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+public final class RadioAlert implements Parcelable {
+
+ public static final class Geocode implements Parcelable {
+
+ private final String mValueName;
+ private final String mValue;
+
+ /**
+ * Constructor of geocode.
+ *
+ * @param valueName Name of geocode value
+ * @param value Value of geocode
+ * @hide
+ */
+ public Geocode(@NonNull String valueName, @NonNull String value) {
+ mValueName = Objects.requireNonNull(valueName, "Geocode value name can not be null");
+ mValue = Objects.requireNonNull(value, "Geocode value can not be null");
+ }
+
+ private Geocode(Parcel in) {
+ mValueName = in.readString8();
+ mValue = in.readString8();
+ }
+
+ public static final @NonNull Creator<Geocode> CREATOR = new Creator<Geocode>() {
+ @Override
+ public Geocode createFromParcel(Parcel in) {
+ return new Geocode(in);
+ }
+
+ @Override
+ public Geocode[] newArray(int size) {
+ return new Geocode[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mValueName);
+ dest.writeString8(mValue);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "Gecode [valueName=" + mValueName + ", value=" + mValue + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mValueName, mValue);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Geocode other)) {
+ return false;
+ }
+
+ return Objects.equals(mValueName, other.mValueName)
+ && Objects.equals(mValue, other.mValue);
+ }
+ }
+
+ public static final class Coordinate implements Parcelable {
+ private final double mLatitude;
+ private final double mLongitude;
+
+ /**
+ * Constructor of coordinate.
+ *
+ * @param latitude Latitude of the coordinate
+ * @param longitude Longitude of the coordinate
+ * @hide
+ */
+ public Coordinate(double latitude, double longitude) {
+ if (latitude < -90.0 || latitude > 90.0) {
+ throw new IllegalArgumentException("Latitude value should be between -90 and 90");
+ }
+ if (longitude < -180.0 || longitude > 180.0) {
+ throw new IllegalArgumentException(
+ "Longitude value should be between -180 and 180");
+ }
+ mLatitude = latitude;
+ mLongitude = longitude;
+ }
+
+ private Coordinate(Parcel in) {
+ mLatitude = in.readDouble();
+ mLongitude = in.readDouble();
+ }
+
+ public static final @NonNull Creator<Coordinate> CREATOR = new Creator<Coordinate>() {
+ @Override
+ public Coordinate createFromParcel(Parcel in) {
+ return new Coordinate(in);
+ }
+
+ @Override
+ public Coordinate[] newArray(int size) {
+ return new Coordinate[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeDouble(mLatitude);
+ dest.writeDouble(mLongitude);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "Coordinate [latitude=" + mLatitude + ", longitude=" + mLongitude + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mLatitude, mLongitude);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Coordinate other)) {
+ return false;
+ }
+ return mLatitude == other.mLatitude && mLongitude == other.mLongitude;
+ }
+ }
+
+ public static final class Polygon implements Parcelable {
+
+ private final List<Coordinate> mCoordinates;
+
+ /**
+ * Constructor of polygon.
+ *
+ * @param coordinates Coordinates the polygon is composed of
+ * @hide
+ */
+ public Polygon(@NonNull List<Coordinate> coordinates) {
+ Objects.requireNonNull(coordinates, "Coordinates can not be null");
+ if (coordinates.size() < 4) {
+ throw new IllegalArgumentException("Number of coordinates must be at least 4");
+ }
+ if (!coordinates.get(0).equals(coordinates.get(coordinates.size() - 1))) {
+ throw new IllegalArgumentException(
+ "The last and first coordinates must be the same");
+ }
+ mCoordinates = coordinates;
+ }
+
+ private Polygon(Parcel in) {
+ mCoordinates = new ArrayList<>();
+ in.readTypedList(mCoordinates, Coordinate.CREATOR);
+ }
+
+ public static final @NonNull Creator<Polygon> CREATOR = new Creator<Polygon>() {
+ @Override
+ public Polygon createFromParcel(Parcel in) {
+ return new Polygon(in);
+ }
+
+ @Override
+ public Polygon[] newArray(int size) {
+ return new Polygon[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedList(mCoordinates);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "Polygon [coordinates=" + mCoordinates + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mCoordinates);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Polygon other)) {
+ return false;
+ }
+ return mCoordinates.equals(other.mCoordinates);
+ }
+ }
+
+ public static final class AlertArea implements Parcelable {
+
+ private final List<Polygon> mPolygons;
+ private final List<Geocode> mGeocodes;
+
+ /**
+ * Constructor of alert area.
+ *
+ * @param polygons Polygons used in alert area
+ * @param geocodes Geocodes used in alert area
+ * @hide
+ */
+ public AlertArea(@NonNull List<Polygon> polygons, @NonNull List<Geocode> geocodes) {
+ mPolygons = Objects.requireNonNull(polygons, "Polygons can not be null");
+ mGeocodes = Objects.requireNonNull(geocodes, "Geocodes can not be null");
+ }
+
+ private AlertArea(Parcel in) {
+ mPolygons = new ArrayList<>();
+ mGeocodes = new ArrayList<>();
+ in.readTypedList(mPolygons, Polygon.CREATOR);
+ in.readTypedList(mGeocodes, Geocode.CREATOR);
+ }
+
+ public static final @NonNull Creator<AlertArea> CREATOR = new Creator<AlertArea>() {
+ @Override
+ public AlertArea createFromParcel(Parcel in) {
+ return new AlertArea(in);
+ }
+
+ @Override
+ public AlertArea[] newArray(int size) {
+ return new AlertArea[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedList(mPolygons);
+ dest.writeTypedList(mGeocodes);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "AlertArea [polygons=" + mPolygons + ", geocodes=" + mGeocodes + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPolygons, mGeocodes);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof AlertArea other)) {
+ return false;
+ }
+
+ return mPolygons.equals(other.mPolygons) && mGeocodes.equals(other.mGeocodes);
+ }
+ }
+
+ public static final class AlertInfo implements Parcelable {
+
+ private final List<Integer> mCategoryList;
+ private final int mUrgency;
+ private final int mSeverity;
+ private final int mCertainty;
+ private final String mTextualMessage;
+ private final List<AlertArea> mAreaList;
+
+ /**
+ * Constructor for alert info.
+ *
+ * @param categoryList Array of categories of the subject event of the alert message
+ * @param urgency The urgency of the subject event of the alert message
+ * @param severity The severity of the subject event of the alert message
+ * @param certainty The certainty of the subject event of the alert message
+ * @param textualMessage Textual descriptions of the subject event
+ * @param areaList The array of geographic areas to which the alert info segment in which
+ * it appears applies
+ * @hide
+ */
+ public AlertInfo(@NonNull List<Integer> categoryList, int urgency,
+ int severity, int certainty,
+ String textualMessage, @NonNull List<AlertArea> areaList) {
+ mCategoryList = Objects.requireNonNull(categoryList, "Category list can not be null");
+ mUrgency = urgency;
+ mSeverity = severity;
+ mCertainty = certainty;
+ mTextualMessage = textualMessage;
+ mAreaList = Objects.requireNonNull(areaList, "Area list can not be null");
+ }
+
+ private AlertInfo(Parcel in) {
+ mCategoryList = in.readArrayList(Integer.class.getClassLoader(), Integer.class);
+ mUrgency = in.readInt();
+ mSeverity = in.readInt();
+ mCertainty = in.readInt();
+ mTextualMessage = in.readString8();
+ mAreaList = new ArrayList<>();
+ in.readTypedList(mAreaList, AlertArea.CREATOR);
+ }
+
+ public static final @NonNull Creator<AlertInfo> CREATOR = new Creator<AlertInfo>() {
+ @Override
+ public AlertInfo createFromParcel(Parcel in) {
+ return new AlertInfo(in);
+ }
+
+ @Override
+ public AlertInfo[] newArray(int size) {
+ return new AlertInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeList(mCategoryList);
+ dest.writeInt(mUrgency);
+ dest.writeInt(mSeverity);
+ dest.writeInt(mCertainty);
+ dest.writeString8(mTextualMessage);
+ dest.writeTypedList(mAreaList);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "AlertInfo [categoryList=" + mCategoryList + ", urgency=" + mUrgency
+ + ", severity=" + mSeverity + ", certainty=" + mCertainty
+ + ", textualMessage=" + mTextualMessage + ", areaList=" + mAreaList + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mCategoryList, mUrgency, mSeverity, mCertainty, mTextualMessage,
+ mAreaList);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof AlertInfo other)) {
+ return false;
+ }
+
+ return mCategoryList.equals(other.mCategoryList) && mUrgency == other.mUrgency
+ && mSeverity == other.mSeverity && mCertainty == other.mCertainty
+ && mTextualMessage.equals(other.mTextualMessage)
+ && mAreaList.equals(other.mAreaList);
+ }
+ }
+
+ private final int mStatus;
+ private final int mMessageType;
+ private final List<AlertInfo> mInfoList;
+ private final int mScope;
+
+ /**
+ * Constructor of radio alert message.
+ *
+ * @param status Status of alert message
+ * @param messageType Message type of alert message
+ * @param infoList List of alert info
+ * @param scope Scope of alert message
+ * @hide
+ */
+ public RadioAlert(int status, int messageType,
+ @NonNull List<AlertInfo> infoList, int scope) {
+ mStatus = status;
+ mMessageType = messageType;
+ mInfoList = Objects.requireNonNull(infoList, "Alert info list can not be null");
+ mScope = scope;
+ }
+
+ private RadioAlert(Parcel in) {
+ mStatus = in.readInt();
+ mMessageType = in.readInt();
+ mInfoList = in.readParcelableList(new ArrayList<>(), AlertInfo.class.getClassLoader(),
+ AlertInfo.class);
+ mScope = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mStatus);
+ dest.writeInt(mMessageType);
+ dest.writeParcelableList(mInfoList, /* flags= */ 0);
+ dest.writeInt(mScope);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "RadioAlert [status=" + mStatus + ", messageType=" + mMessageType
+ + ", infoList= " + mInfoList + ", scope=" + mScope + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mStatus, mMessageType, mInfoList, mScope);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof RadioAlert other)) {
+ return false;
+ }
+
+ return mStatus == other.mStatus && mMessageType == other.mMessageType
+ && mInfoList.equals(other.mInfoList) && mScope == other.mScope;
+ }
+
+ public static final @NonNull Creator<RadioAlert> CREATOR = new Creator<RadioAlert>() {
+ @Override
+ public RadioAlert createFromParcel(Parcel in) {
+ return new RadioAlert(in);
+ }
+
+ @Override
+ public RadioAlert[] newArray(int size) {
+ return new RadioAlert[size];
+ }
+ };
+}
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 49ab15a40a8e..50121278f0e6 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -21,6 +21,7 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Intent;
import android.util.ArrayMap;
import android.util.Log;
import android.util.MathUtils;
@@ -401,6 +402,9 @@ public class BaseBundle {
synchronized (this) {
object = unwrapLazyValueFromMapLocked(i, clazz, itemTypes);
}
+ if ((mFlags & Bundle.FLAG_VERIFY_TOKENS_PRESENT) != 0) {
+ Intent.maybeMarkAsMissingCreatorToken(object);
+ }
}
return (clazz != null) ? clazz.cast(object) : (T) object;
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index d3ba73ed1421..a89483394611 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1278,6 +1278,41 @@ public class Build {
/** @hide */
@IntDef(value = {
+ VERSION_CODES_FULL.BASE,
+ VERSION_CODES_FULL.BASE_1_1,
+ VERSION_CODES_FULL.CUPCAKE,
+ VERSION_CODES_FULL.DONUT,
+ VERSION_CODES_FULL.ECLAIR,
+ VERSION_CODES_FULL.ECLAIR_0_1,
+ VERSION_CODES_FULL.ECLAIR_MR1,
+ VERSION_CODES_FULL.FROYO,
+ VERSION_CODES_FULL.GINGERBREAD,
+ VERSION_CODES_FULL.GINGERBREAD_MR1,
+ VERSION_CODES_FULL.HONEYCOMB,
+ VERSION_CODES_FULL.HONEYCOMB_MR1,
+ VERSION_CODES_FULL.HONEYCOMB_MR2,
+ VERSION_CODES_FULL.ICE_CREAM_SANDWICH,
+ VERSION_CODES_FULL.ICE_CREAM_SANDWICH_MR1,
+ VERSION_CODES_FULL.JELLY_BEAN,
+ VERSION_CODES_FULL.JELLY_BEAN_MR1,
+ VERSION_CODES_FULL.JELLY_BEAN_MR2,
+ VERSION_CODES_FULL.KITKAT,
+ VERSION_CODES_FULL.KITKAT_WATCH,
+ VERSION_CODES_FULL.LOLLIPOP,
+ VERSION_CODES_FULL.LOLLIPOP_MR1,
+ VERSION_CODES_FULL.M,
+ VERSION_CODES_FULL.N,
+ VERSION_CODES_FULL.N_MR1,
+ VERSION_CODES_FULL.O,
+ VERSION_CODES_FULL.O_MR1,
+ VERSION_CODES_FULL.P,
+ VERSION_CODES_FULL.Q,
+ VERSION_CODES_FULL.R,
+ VERSION_CODES_FULL.S,
+ VERSION_CODES_FULL.S_V2,
+ VERSION_CODES_FULL.TIRAMISU,
+ VERSION_CODES_FULL.UPSIDE_DOWN_CAKE,
+ VERSION_CODES_FULL.VANILLA_ICE_CREAM,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SdkIntFull {}
@@ -1299,6 +1334,186 @@ public class Build {
// Use the last 5 digits for the minor version. This allows the
// minor version to be set to CUR_DEVELOPMENT.
private static final int SDK_INT_MULTIPLIER = 100000;
+
+ /**
+ * Android 1.0.
+ */
+ public static final int BASE = VERSION_CODES.BASE * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 2.0.
+ */
+ public static final int BASE_1_1 = VERSION_CODES.BASE_1_1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 3.0.
+ */
+ public static final int CUPCAKE = VERSION_CODES.CUPCAKE * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 4.0.
+ */
+ public static final int DONUT = VERSION_CODES.DONUT * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 5.0.
+ */
+ public static final int ECLAIR = VERSION_CODES.ECLAIR * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 6.0.
+ */
+ public static final int ECLAIR_0_1 = VERSION_CODES.ECLAIR_0_1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 7.0.
+ */
+ public static final int ECLAIR_MR1 = VERSION_CODES.ECLAIR_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 8.0.
+ */
+ public static final int FROYO = VERSION_CODES.FROYO * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 9.0.
+ */
+ public static final int GINGERBREAD = VERSION_CODES.GINGERBREAD * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 10.0.
+ */
+ public static final int GINGERBREAD_MR1 =
+ VERSION_CODES.GINGERBREAD_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 11.0.
+ */
+ public static final int HONEYCOMB = VERSION_CODES.HONEYCOMB * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 12.0.
+ */
+ public static final int HONEYCOMB_MR1 = VERSION_CODES.HONEYCOMB_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 13.0.
+ */
+ public static final int HONEYCOMB_MR2 = VERSION_CODES.HONEYCOMB_MR2 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 14.0.
+ */
+ public static final int ICE_CREAM_SANDWICH =
+ VERSION_CODES.ICE_CREAM_SANDWICH * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 15.0.
+ */
+ public static final int ICE_CREAM_SANDWICH_MR1 =
+ VERSION_CODES.ICE_CREAM_SANDWICH_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 16.0.
+ */
+ public static final int JELLY_BEAN = VERSION_CODES.JELLY_BEAN * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 17.0.
+ */
+ public static final int JELLY_BEAN_MR1 = VERSION_CODES.JELLY_BEAN_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 18.0.
+ */
+ public static final int JELLY_BEAN_MR2 = VERSION_CODES.JELLY_BEAN_MR2 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 19.0.
+ */
+ public static final int KITKAT = VERSION_CODES.KITKAT * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 20.0.
+ */
+ public static final int KITKAT_WATCH = VERSION_CODES.KITKAT_WATCH * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 21.0.
+ */
+ public static final int LOLLIPOP = VERSION_CODES.LOLLIPOP * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 22.0.
+ */
+ public static final int LOLLIPOP_MR1 = VERSION_CODES.LOLLIPOP_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 23.0.
+ */
+ public static final int M = VERSION_CODES.M * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 24.0.
+ */
+ public static final int N = VERSION_CODES.N * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 25.0.
+ */
+ public static final int N_MR1 = VERSION_CODES.N_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 26.0.
+ */
+ public static final int O = VERSION_CODES.O * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 27.0.
+ */
+ public static final int O_MR1 = VERSION_CODES.O_MR1 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 28.0.
+ */
+ public static final int P = VERSION_CODES.P * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 29.0.
+ */
+ public static final int Q = VERSION_CODES.Q * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 30.0.
+ */
+ public static final int R = VERSION_CODES.R * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 31.0.
+ */
+ public static final int S = VERSION_CODES.S * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 32.0.
+ */
+ public static final int S_V2 = VERSION_CODES.S_V2 * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 33.0.
+ */
+ public static final int TIRAMISU = VERSION_CODES.TIRAMISU * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 34.0.
+ */
+ public static final int UPSIDE_DOWN_CAKE =
+ VERSION_CODES.UPSIDE_DOWN_CAKE * SDK_INT_MULTIPLIER;
+
+ /**
+ * Android 35.0.
+ */
+ public static final int VANILLA_ICE_CREAM =
+ VERSION_CODES.VANILLA_ICE_CREAM * SDK_INT_MULTIPLIER;
}
/**
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index ed4037c7d246..c18fb0c38b82 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -62,6 +62,12 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
@VisibleForTesting
static final int FLAG_HAS_BINDERS = 1 << 12;
+ /**
+ * Indicates there may be intents with creator tokens contained in this bundle. When unparceled,
+ * they should be verified if tokens are missing or invalid.
+ */
+ static final int FLAG_VERIFY_TOKENS_PRESENT = 1 << 13;
+
/**
* Status when the Bundle can <b>assert</b> that the underlying Parcel DOES NOT contain
@@ -274,6 +280,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
return orig;
}
+ /** {@hide} */
+ public void setIsIntentExtra() {
+ mFlags |= FLAG_VERIFY_TOKENS_PRESENT;
+ }
+
/**
* Mark if this Bundle is okay to "defuse." That is, it's okay for system
* processes to ignore any {@link BadParcelableException} encountered when
diff --git a/core/java/android/os/CombinedVibration.java b/core/java/android/os/CombinedVibration.java
index 77d6cb762e06..f1d3957cc919 100644
--- a/core/java/android/os/CombinedVibration.java
+++ b/core/java/android/os/CombinedVibration.java
@@ -17,6 +17,7 @@
package android.os;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.vibrator.Flags;
import android.util.SparseArray;
@@ -28,6 +29,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.StringJoiner;
+import java.util.function.Function;
/**
* A CombinedVibration describes a combination of haptic effects to be performed by one or more
@@ -114,6 +116,17 @@ public abstract class CombinedVibration implements Parcelable {
public abstract long getDuration();
/**
+ * Gets the estimated duration of the combined vibration in milliseconds.
+ *
+ * <p>For effects with hardware-dependent constants (e.g. primitive compositions), this returns
+ * the estimated duration based on the {@link VibratorInfo}. For all other effects this will
+ * return the same as {@link #getDuration()}.
+ *
+ * @hide
+ */
+ public abstract long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos);
+
+ /**
* Returns true if this effect could represent a touch haptic feedback.
*
* <p>It is strongly recommended that an instance of {@link VibrationAttributes} is specified
@@ -383,6 +396,23 @@ public abstract class CombinedVibration implements Parcelable {
/** @hide */
@Override
+ public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) {
+ if (vibratorInfos == null) {
+ return getDuration();
+ }
+ long maxDuration = 0;
+ for (int i = 0; i < vibratorInfos.size(); i++) {
+ long duration = mEffect.getDuration(vibratorInfos.valueAt(i));
+ if ((duration == Long.MAX_VALUE) || (duration < 0)) {
+ return duration;
+ }
+ maxDuration = Math.max(maxDuration, duration);
+ }
+ return maxDuration;
+ }
+
+ /** @hide */
+ @Override
public boolean isHapticFeedbackCandidate() {
return mEffect.isHapticFeedbackCandidate();
}
@@ -531,10 +561,27 @@ public abstract class CombinedVibration implements Parcelable {
@Override
public long getDuration() {
+ return getDuration(idx -> mEffects.valueAt(idx).getDuration());
+ }
+
+ /** @hide */
+ @Override
+ public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) {
+ if (vibratorInfos == null) {
+ return getDuration();
+ }
+ return getDuration(idx -> {
+ VibrationEffect effect = mEffects.valueAt(idx);
+ VibratorInfo info = vibratorInfos.get(mEffects.keyAt(idx));
+ return effect.getDuration(info);
+ });
+ }
+
+ private long getDuration(Function<Integer, Long> durationFn) {
long maxDuration = Long.MIN_VALUE;
boolean hasUnknownStep = false;
for (int i = 0; i < mEffects.size(); i++) {
- long duration = mEffects.valueAt(i).getDuration();
+ long duration = durationFn.apply(i);
if (duration == Long.MAX_VALUE) {
// If any duration is repeating, this combination duration is also repeating.
return duration;
@@ -750,12 +797,21 @@ public abstract class CombinedVibration implements Parcelable {
@Override
public long getDuration() {
+ return getDuration(CombinedVibration::getDuration);
+ }
+
+ /** @hide */
+ @Override
+ public long getDuration(@Nullable SparseArray<VibratorInfo> vibratorInfos) {
+ return getDuration(effect -> effect.getDuration(vibratorInfos));
+ }
+
+ private long getDuration(Function<CombinedVibration, Long> durationFn) {
boolean hasUnknownStep = false;
long durations = 0;
final int effectCount = mEffects.size();
for (int i = 0; i < effectCount; i++) {
- CombinedVibration effect = mEffects.get(i);
- long duration = effect.getDuration();
+ long duration = durationFn.apply(mEffects.get(i));
if (duration == Long.MAX_VALUE) {
// If any duration is repeating, this combination duration is also repeating.
return duration;
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index a55398ac9752..ef1e6c9405f3 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -114,6 +114,7 @@ public final class Debug
"opengl-tracing",
"view-hierarchy",
"support_boot_stages",
+ "app_info",
};
/**
@@ -1016,14 +1017,14 @@ public final class Debug
// send VM_START.
System.out.println("Waiting for debugger first packet");
- mWaiting = true;
+ setWaitingForDebugger(true);
while (!isDebuggerConnected()) {
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
}
}
- mWaiting = false;
+ setWaitingForDebugger(false);
System.out.println("Debug.suspendAllAndSentVmStart");
VMDebug.suspendAllAndSendVmStart();
@@ -1049,12 +1050,12 @@ public final class Debug
Chunk waitChunk = new Chunk(ChunkHandler.type("WAIT"), data, 0, 1);
DdmServer.sendChunk(waitChunk);
- mWaiting = true;
+ setWaitingForDebugger(true);
while (!isDebuggerConnected()) {
try { Thread.sleep(SPIN_DELAY); }
catch (InterruptedException ie) {}
}
- mWaiting = false;
+ setWaitingForDebugger(false);
System.out.println("Debugger has connected");
@@ -1112,6 +1113,16 @@ public final class Debug
}
/**
+ * Set whether the app is waiting for a debugger to connect
+ *
+ * @hide
+ */
+ private static void setWaitingForDebugger(boolean waiting) {
+ mWaiting = waiting;
+ VMDebug.setWaitingForDebugger(waiting);
+ }
+
+ /**
* Returns an array of strings that identify Framework features. This is
* used by DDMS to determine what sorts of operations the Framework can
* perform.
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 346ee7ca4f87..71d29af6cf02 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -41,6 +41,7 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
import com.android.sdksandbox.flags.Flags;
+import dalvik.system.VMDebug;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
@@ -1410,6 +1411,7 @@ public class Process {
public static void setArgV0(@NonNull String text) {
sArgV0 = text;
setArgV0Native(text);
+ VMDebug.setCurrentProcessName(text);
}
private static native void setArgV0Native(String text);
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index f82c8221e4f9..769cbdd9886d 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -16,18 +16,11 @@
package android.os;
-import android.annotation.FlaggedApi;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.ArrayMap;
import android.util.Slog;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -37,7 +30,7 @@ import java.util.function.Consumer;
* {@link android.app.Service} to its clients. In particular, this:
*
* <ul>
- * <li> Keeps track of a set of registered {@link IInterface} objects,
+ * <li> Keeps track of a set of registered {@link IInterface} callbacks,
* taking care to identify them through their underlying unique {@link IBinder}
* (by calling {@link IInterface#asBinder IInterface.asBinder()}.
* <li> Attaches a {@link IBinder.DeathRecipient IBinder.DeathRecipient} to
@@ -54,7 +47,7 @@ import java.util.function.Consumer;
* the registered clients, use {@link #beginBroadcast},
* {@link #getBroadcastItem}, and {@link #finishBroadcast}.
*
- * <p>If a registered interface's process goes away, this class will take
+ * <p>If a registered callback's process goes away, this class will take
* care of automatically removing it from the list. If you want to do
* additional work in this situation, you can create a subclass that
* implements the {@link #onCallbackDied} method.
@@ -63,310 +56,78 @@ import java.util.function.Consumer;
public class RemoteCallbackList<E extends IInterface> {
private static final String TAG = "RemoteCallbackList";
- private static final int DEFAULT_MAX_QUEUE_SIZE = 1000;
-
-
- /**
- * @hide
- */
- @IntDef(prefix = {"FROZEN_CALLEE_POLICY_"}, value = {
- FROZEN_CALLEE_POLICY_UNSET,
- FROZEN_CALLEE_POLICY_ENQUEUE_ALL,
- FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT,
- FROZEN_CALLEE_POLICY_DROP,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface FrozenCalleePolicy {
- }
-
- /**
- * Callbacks are invoked immediately regardless of the frozen state of the target process.
- *
- * Not recommended. Only exists for backward-compatibility. This represents the behavior up to
- * SDK 35. Starting with SDK 36, clients should set a policy to govern callback invocations when
- * recipients are frozen.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public static final int FROZEN_CALLEE_POLICY_UNSET = 0;
-
- /**
- * When the callback recipient's process is frozen, callbacks are enqueued so they're invoked
- * after the recipient is unfrozen.
- *
- * This is commonly used when the recipient wants to receive all callbacks without losing any
- * history, e.g. the recipient maintains a running count of events that occurred.
- *
- * Queued callbacks are invoked in the order they were originally broadcasted.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public static final int FROZEN_CALLEE_POLICY_ENQUEUE_ALL = 1;
-
- /**
- * When the callback recipient's process is frozen, only the most recent callback is enqueued,
- * which is later invoked after the recipient is unfrozen.
- *
- * This can be used when only the most recent state matters, for instance when clients are
- * listening to screen brightness changes.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public static final int FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT = 2;
-
- /**
- * When the callback recipient's process is frozen, callbacks are suppressed as if they never
- * happened.
- *
- * This could be useful in the case where the recipient wishes to react to callbacks only when
- * they occur while the recipient is not frozen. For example, certain network events are only
- * worth responding to if the response can be immediate. Another example is recipients having
- * another way of getting the latest state once it's unfrozen. Therefore there is no need to
- * save callbacks that happened while the recipient was frozen.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public static final int FROZEN_CALLEE_POLICY_DROP = 3;
-
@UnsupportedAppUsage
- /*package*/ ArrayMap<IBinder, Interface> mInterfaces = new ArrayMap<IBinder, Interface>();
+ /*package*/ ArrayMap<IBinder, Callback> mCallbacks
+ = new ArrayMap<IBinder, Callback>();
private Object[] mActiveBroadcast;
private int mBroadcastCount = -1;
private boolean mKilled = false;
private StringBuilder mRecentCallers;
- private final @FrozenCalleePolicy int mFrozenCalleePolicy;
- private final int mMaxQueueSize;
-
- private final class Interface implements IBinder.DeathRecipient,
- IBinder.FrozenStateChangeCallback {
- final IBinder mBinder;
- final E mInterface;
+ private final class Callback implements IBinder.DeathRecipient {
+ final E mCallback;
final Object mCookie;
- final Queue<Consumer<E>> mCallbackQueue;
- int mCurrentState = IBinder.FrozenStateChangeCallback.STATE_UNFROZEN;
- Interface(E callbackInterface, Object cookie) {
- mBinder = callbackInterface.asBinder();
- mInterface = callbackInterface;
+ Callback(E callback, Object cookie) {
+ mCallback = callback;
mCookie = cookie;
- mCallbackQueue = mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_ENQUEUE_ALL
- || mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT
- ? new ConcurrentLinkedQueue<>() : null;
- }
-
- @Override
- public synchronized void onFrozenStateChanged(@NonNull IBinder who, int state) {
- if (state == STATE_UNFROZEN && mCallbackQueue != null) {
- while (!mCallbackQueue.isEmpty()) {
- Consumer<E> callback = mCallbackQueue.poll();
- callback.accept(mInterface);
- }
- }
- mCurrentState = state;
- }
-
- void addCallback(@NonNull Consumer<E> callback) {
- if (mFrozenCalleePolicy == FROZEN_CALLEE_POLICY_UNSET) {
- callback.accept(mInterface);
- return;
- }
- synchronized (this) {
- if (mCurrentState == STATE_UNFROZEN) {
- callback.accept(mInterface);
- return;
- }
- switch (mFrozenCalleePolicy) {
- case FROZEN_CALLEE_POLICY_ENQUEUE_ALL:
- if (mCallbackQueue.size() >= mMaxQueueSize) {
- mCallbackQueue.poll();
- }
- mCallbackQueue.offer(callback);
- break;
- case FROZEN_CALLEE_POLICY_ENQUEUE_MOST_RECENT:
- mCallbackQueue.clear();
- mCallbackQueue.offer(callback);
- break;
- case FROZEN_CALLEE_POLICY_DROP:
- // Do nothing. Just ignore the callback.
- break;
- case FROZEN_CALLEE_POLICY_UNSET:
- // Do nothing. Should have returned at the start of the method.
- break;
- }
- }
- }
-
- public void maybeSubscribeToFrozenCallback() throws RemoteException {
- if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
- mBinder.addFrozenStateChangeCallback(this);
- }
- }
-
- public void maybeUnsubscribeFromFrozenCallback() {
- if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
- mBinder.removeFrozenStateChangeCallback(this);
- }
}
public void binderDied() {
- synchronized (mInterfaces) {
- mInterfaces.remove(mBinder);
- maybeUnsubscribeFromFrozenCallback();
- }
- onCallbackDied(mInterface, mCookie);
- }
- }
-
- /**
- * Builder for {@link RemoteCallbackList}.
- *
- * @param <E> The remote callback interface type.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public static final class Builder<E extends IInterface> {
- private @FrozenCalleePolicy int mFrozenCalleePolicy;
- private int mMaxQueueSize = DEFAULT_MAX_QUEUE_SIZE;
-
- /**
- * Creates a Builder for {@link RemoteCallbackList}.
- *
- * @param frozenCalleePolicy When the callback recipient's process is frozen, this parameter
- * specifies when/whether callbacks are invoked. It's important to choose a strategy that's
- * right for the use case. Leaving the policy unset with {@link #FROZEN_CALLEE_POLICY_UNSET}
- * is not recommended as it allows callbacks to be invoked while the recipient is frozen.
- */
- public Builder(@FrozenCalleePolicy int frozenCalleePolicy) {
- mFrozenCalleePolicy = frozenCalleePolicy;
- }
-
- /**
- * Sets the max queue size.
- *
- * @param maxQueueSize The max size limit on the queue that stores callbacks added when the
- * recipient's process is frozen. Once the limit is reached, the oldest callback is dropped
- * to keep the size under the limit. Should only be called for
- * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}.
- *
- * @return This builder.
- * @throws IllegalArgumentException if the maxQueueSize is not positive.
- * @throws UnsupportedOperationException if frozenCalleePolicy is not
- * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}.
- */
- public @NonNull Builder setMaxQueueSize(int maxQueueSize) {
- if (maxQueueSize <= 0) {
- throw new IllegalArgumentException("maxQueueSize must be positive");
- }
- if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_ENQUEUE_ALL) {
- throw new UnsupportedOperationException(
- "setMaxQueueSize can only be called for FROZEN_CALLEE_POLICY_ENQUEUE_ALL");
+ synchronized (mCallbacks) {
+ mCallbacks.remove(mCallback.asBinder());
}
- mMaxQueueSize = maxQueueSize;
- return this;
- }
-
- /**
- * Builds and returns a {@link RemoteCallbackList}.
- *
- * @return The built {@link RemoteCallbackList} object.
- */
- public @NonNull RemoteCallbackList<E> build() {
- return new RemoteCallbackList<E>(mFrozenCalleePolicy, mMaxQueueSize);
+ onCallbackDied(mCallback, mCookie);
}
}
/**
- * Returns the frozen callee policy.
- *
- * @return The frozen callee policy.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public @FrozenCalleePolicy int getFrozenCalleePolicy() {
- return mFrozenCalleePolicy;
- }
-
- /**
- * Returns the max queue size.
- *
- * @return The max queue size.
- */
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public int getMaxQueueSize() {
- return mMaxQueueSize;
- }
-
- /**
- * Creates a RemoteCallbackList with {@link #FROZEN_CALLEE_POLICY_UNSET}. This is equivalent to
- * <pre>
- * new RemoteCallbackList.Build(RemoteCallbackList.FROZEN_CALLEE_POLICY_UNSET).build()
- * </pre>
- */
- public RemoteCallbackList() {
- this(FROZEN_CALLEE_POLICY_UNSET, DEFAULT_MAX_QUEUE_SIZE);
- }
-
- /**
- * Creates a RemoteCallbackList with the specified frozen callee policy.
- *
- * @param frozenCalleePolicy When the callback recipient's process is frozen, this parameter
- * specifies when/whether callbacks are invoked. It's important to choose a strategy that's
- * right for the use case. Leaving the policy unset with {@link #FROZEN_CALLEE_POLICY_UNSET}
- * is not recommended as it allows callbacks to be invoked while the recipient is frozen.
- *
- * @param maxQueueSize The max size limit on the queue that stores callbacks added when the
- * recipient's process is frozen. Once the limit is reached, the oldest callbacks would be
- * dropped to keep the size under limit. Ignored except for
- * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}.
- */
- private RemoteCallbackList(@FrozenCalleePolicy int frozenCalleePolicy, int maxQueueSize) {
- mFrozenCalleePolicy = frozenCalleePolicy;
- mMaxQueueSize = maxQueueSize;
- }
-
- /**
* Simple version of {@link RemoteCallbackList#register(E, Object)}
* that does not take a cookie object.
*/
- public boolean register(E callbackInterface) {
- return register(callbackInterface, null);
+ public boolean register(E callback) {
+ return register(callback, null);
}
/**
- * Add a new interface to the list. This interface will remain in the list
+ * Add a new callback to the list. This callback will remain in the list
* until a corresponding call to {@link #unregister} or its hosting process
- * goes away. If the interface was already registered (determined by
- * checking to see if the {@link IInterface#asBinder callbackInterface.asBinder()}
- * object is already in the list), then it will be replaced with the new interface.
+ * goes away. If the callback was already registered (determined by
+ * checking to see if the {@link IInterface#asBinder callback.asBinder()}
+ * object is already in the list), then it will be replaced with the new callback.
* Registrations are not counted; a single call to {@link #unregister}
- * will remove an interface after any number calls to register it.
+ * will remove a callback after any number calls to register it.
*
- * @param callbackInterface The callback interface to be added to the list. Must
+ * @param callback The callback interface to be added to the list. Must
* not be null -- passing null here will cause a NullPointerException.
* Most services will want to check for null before calling this with
* an object given from a client, so that clients can't crash the
* service with bad data.
*
* @param cookie Optional additional data to be associated with this
- * interface.
+ * callback.
*
- * @return Returns true if the interface was successfully added to the list.
+ * @return Returns true if the callback was successfully added to the list.
* Returns false if it was not added, either because {@link #kill} had
- * previously been called or the interface's process has gone away.
+ * previously been called or the callback's process has gone away.
*
* @see #unregister
* @see #kill
* @see #onCallbackDied
*/
- public boolean register(E callbackInterface, Object cookie) {
- synchronized (mInterfaces) {
+ public boolean register(E callback, Object cookie) {
+ synchronized (mCallbacks) {
if (mKilled) {
return false;
}
// Flag unusual case that could be caused by a leak. b/36778087
- logExcessiveInterfaces();
- IBinder binder = callbackInterface.asBinder();
+ logExcessiveCallbacks();
+ IBinder binder = callback.asBinder();
try {
- Interface i = new Interface(callbackInterface, cookie);
- unregister(callbackInterface);
- binder.linkToDeath(i, 0);
- i.maybeSubscribeToFrozenCallback();
- mInterfaces.put(binder, i);
+ Callback cb = new Callback(callback, cookie);
+ unregister(callback);
+ binder.linkToDeath(cb, 0);
+ mCallbacks.put(binder, cb);
return true;
} catch (RemoteException e) {
return false;
@@ -375,28 +136,27 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
- * Remove from the list an interface that was previously added with
+ * Remove from the list a callback that was previously added with
* {@link #register}. This uses the
- * {@link IInterface#asBinder callbackInterface.asBinder()} object to correctly
+ * {@link IInterface#asBinder callback.asBinder()} object to correctly
* find the previous registration.
* Registrations are not counted; a single unregister call will remove
- * an interface after any number calls to {@link #register} for it.
+ * a callback after any number calls to {@link #register} for it.
*
- * @param callbackInterface The interface to be removed from the list. Passing
+ * @param callback The callback to be removed from the list. Passing
* null here will cause a NullPointerException, so you will generally want
* to check for null before calling.
*
- * @return Returns true if the interface was found and unregistered. Returns
- * false if the given interface was not found on the list.
+ * @return Returns true if the callback was found and unregistered. Returns
+ * false if the given callback was not found on the list.
*
* @see #register
*/
- public boolean unregister(E callbackInterface) {
- synchronized (mInterfaces) {
- Interface i = mInterfaces.remove(callbackInterface.asBinder());
- if (i != null) {
- i.mInterface.asBinder().unlinkToDeath(i, 0);
- i.maybeUnsubscribeFromFrozenCallback();
+ public boolean unregister(E callback) {
+ synchronized (mCallbacks) {
+ Callback cb = mCallbacks.remove(callback.asBinder());
+ if (cb != null) {
+ cb.mCallback.asBinder().unlinkToDeath(cb, 0);
return true;
}
return false;
@@ -404,21 +164,20 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
- * Disable this interface list. All registered interfaces are unregistered,
+ * Disable this callback list. All registered callbacks are unregistered,
* and the list is disabled so that future calls to {@link #register} will
* fail. This should be used when a Service is stopping, to prevent clients
- * from registering interfaces after it is stopped.
+ * from registering callbacks after it is stopped.
*
* @see #register
*/
public void kill() {
- synchronized (mInterfaces) {
- for (int cbi = mInterfaces.size() - 1; cbi >= 0; cbi--) {
- Interface i = mInterfaces.valueAt(cbi);
- i.mInterface.asBinder().unlinkToDeath(i, 0);
- i.maybeUnsubscribeFromFrozenCallback();
+ synchronized (mCallbacks) {
+ for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
+ Callback cb = mCallbacks.valueAt(cbi);
+ cb.mCallback.asBinder().unlinkToDeath(cb, 0);
}
- mInterfaces.clear();
+ mCallbacks.clear();
mKilled = true;
}
}
@@ -427,15 +186,15 @@ public class RemoteCallbackList<E extends IInterface> {
* Old version of {@link #onCallbackDied(E, Object)} that
* does not provide a cookie.
*/
- public void onCallbackDied(E callbackInterface) {
+ public void onCallbackDied(E callback) {
}
/**
- * Called when the process hosting an interface in the list has gone away.
+ * Called when the process hosting a callback in the list has gone away.
* The default implementation calls {@link #onCallbackDied(E)}
* for backwards compatibility.
*
- * @param callbackInterface The interface whose process has died. Note that, since
+ * @param callback The callback whose process has died. Note that, since
* its process has died, you can not make any calls on to this interface.
* You can, however, retrieve its IBinder and compare it with another
* IBinder to see if it is the same object.
@@ -444,15 +203,13 @@ public class RemoteCallbackList<E extends IInterface> {
*
* @see #register
*/
- public void onCallbackDied(E callbackInterface, Object cookie) {
- onCallbackDied(callbackInterface);
+ public void onCallbackDied(E callback, Object cookie) {
+ onCallbackDied(callback);
}
/**
- * Use {@link #broadcast(Consumer)} instead to ensure proper handling of frozen processes.
- *
- * Prepare to start making calls to the currently registered interfaces.
- * This creates a copy of the interface list, which you can retrieve items
+ * Prepare to start making calls to the currently registered callbacks.
+ * This creates a copy of the callback list, which you can retrieve items
* from using {@link #getBroadcastItem}. Note that only one broadcast can
* be active at a time, so you must be sure to always call this from the
* same thread (usually by scheduling with {@link Handler}) or
@@ -462,56 +219,44 @@ public class RemoteCallbackList<E extends IInterface> {
* <p>A typical loop delivering a broadcast looks like this:
*
* <pre>
- * int i = interfaces.beginBroadcast();
+ * int i = callbacks.beginBroadcast();
* while (i &gt; 0) {
* i--;
* try {
- * interfaces.getBroadcastItem(i).somethingHappened();
+ * callbacks.getBroadcastItem(i).somethingHappened();
* } catch (RemoteException e) {
* // The RemoteCallbackList will take care of removing
* // the dead object for us.
* }
* }
- * interfaces.finishBroadcast();</pre>
+ * callbacks.finishBroadcast();</pre>
*
- * Note that this method is only supported for {@link #FROZEN_CALLEE_POLICY_UNSET}. For other
- * policies use {@link #broadcast(Consumer)} instead.
- *
- * @return Returns the number of interfaces in the broadcast, to be used
+ * @return Returns the number of callbacks in the broadcast, to be used
* with {@link #getBroadcastItem} to determine the range of indices you
* can supply.
*
- * @throws UnsupportedOperationException if an frozen callee policy is set.
- *
* @see #getBroadcastItem
* @see #finishBroadcast
*/
public int beginBroadcast() {
- if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) {
- throw new UnsupportedOperationException();
- }
- return beginBroadcastInternal();
- }
-
- private int beginBroadcastInternal() {
- synchronized (mInterfaces) {
+ synchronized (mCallbacks) {
if (mBroadcastCount > 0) {
throw new IllegalStateException(
"beginBroadcast() called while already in a broadcast");
}
- final int n = mBroadcastCount = mInterfaces.size();
- if (n <= 0) {
+ final int N = mBroadcastCount = mCallbacks.size();
+ if (N <= 0) {
return 0;
}
Object[] active = mActiveBroadcast;
- if (active == null || active.length < n) {
- mActiveBroadcast = active = new Object[n];
+ if (active == null || active.length < N) {
+ mActiveBroadcast = active = new Object[N];
}
- for (int i = 0; i < n; i++) {
- active[i] = mInterfaces.valueAt(i);
+ for (int i=0; i<N; i++) {
+ active[i] = mCallbacks.valueAt(i);
}
- return n;
+ return N;
}
}
@@ -522,23 +267,24 @@ public class RemoteCallbackList<E extends IInterface> {
* calling {@link #finishBroadcast}.
*
* <p>Note that it is possible for the process of one of the returned
- * interfaces to go away before you call it, so you will need to catch
+ * callbacks to go away before you call it, so you will need to catch
* {@link RemoteException} when calling on to the returned object.
- * The interface list itself, however, will take care of unregistering
+ * The callback list itself, however, will take care of unregistering
* these objects once it detects that it is no longer valid, so you can
* handle such an exception by simply ignoring it.
*
- * @param index Which of the registered interfaces you would like to
+ * @param index Which of the registered callbacks you would like to
* retrieve. Ranges from 0 to {@link #beginBroadcast}-1, inclusive.
*
- * @return Returns the interface that you can call. This will always be non-null.
+ * @return Returns the callback interface that you can call. This will
+ * always be non-null.
*
* @see #beginBroadcast
*/
public E getBroadcastItem(int index) {
- return ((Interface) mActiveBroadcast[index]).mInterface;
+ return ((Callback)mActiveBroadcast[index]).mCallback;
}
-
+
/**
* Retrieve the cookie associated with the item
* returned by {@link #getBroadcastItem(int)}.
@@ -546,7 +292,7 @@ public class RemoteCallbackList<E extends IInterface> {
* @see #getBroadcastItem
*/
public Object getBroadcastCookie(int index) {
- return ((Interface) mActiveBroadcast[index]).mCookie;
+ return ((Callback)mActiveBroadcast[index]).mCookie;
}
/**
@@ -557,7 +303,7 @@ public class RemoteCallbackList<E extends IInterface> {
* @see #beginBroadcast
*/
public void finishBroadcast() {
- synchronized (mInterfaces) {
+ synchronized (mCallbacks) {
if (mBroadcastCount < 0) {
throw new IllegalStateException(
"finishBroadcast() called outside of a broadcast");
@@ -576,18 +322,16 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
- * Performs {@code callback} on each registered interface.
+ * Performs {@code action} on each callback, calling
+ * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
*
- * This is equivalent to #beginBroadcast, followed by iterating over the items using
- * #getBroadcastItem and then @finishBroadcast, except that this method supports
- * frozen callee policies.
+ * @hide
*/
- @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK)
- public void broadcast(@NonNull Consumer<E> callback) {
- int itemCount = beginBroadcastInternal();
+ public void broadcast(Consumer<E> action) {
+ int itemCount = beginBroadcast();
try {
for (int i = 0; i < itemCount; i++) {
- ((Interface) mActiveBroadcast[i]).addCallback(callback);
+ action.accept(getBroadcastItem(i));
}
} finally {
finishBroadcast();
@@ -595,16 +339,16 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
- * Performs {@code callback} for each cookie associated with an interface, calling
+ * Performs {@code action} for each cookie associated with a callback, calling
* {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
*
* @hide
*/
- public <C> void broadcastForEachCookie(Consumer<C> callback) {
+ public <C> void broadcastForEachCookie(Consumer<C> action) {
int itemCount = beginBroadcast();
try {
for (int i = 0; i < itemCount; i++) {
- callback.accept((C) getBroadcastCookie(i));
+ action.accept((C) getBroadcastCookie(i));
}
} finally {
finishBroadcast();
@@ -612,16 +356,16 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
- * Performs {@code callback} on each interface and associated cookie, calling {@link
+ * Performs {@code action} on each callback and associated cookie, calling {@link
* #beginBroadcast()}/{@link #finishBroadcast()} before/after looping.
*
* @hide
*/
- public <C> void broadcast(BiConsumer<E, C> callback) {
+ public <C> void broadcast(BiConsumer<E, C> action) {
int itemCount = beginBroadcast();
try {
for (int i = 0; i < itemCount; i++) {
- callback.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
+ action.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
}
} finally {
finishBroadcast();
@@ -629,10 +373,10 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
- * Returns the number of registered interfaces. Note that the number of registered
- * interfaces may differ from the value returned by {@link #beginBroadcast()} since
- * the former returns the number of interfaces registered at the time of the call
- * and the second the number of interfaces to which the broadcast will be delivered.
+ * Returns the number of registered callbacks. Note that the number of registered
+ * callbacks may differ from the value returned by {@link #beginBroadcast()} since
+ * the former returns the number of callbacks registered at the time of the call
+ * and the second the number of callback to which the broadcast will be delivered.
* <p>
* This function is useful to decide whether to schedule a broadcast if this
* requires doing some work which otherwise would not be performed.
@@ -641,39 +385,39 @@ public class RemoteCallbackList<E extends IInterface> {
* @return The size.
*/
public int getRegisteredCallbackCount() {
- synchronized (mInterfaces) {
+ synchronized (mCallbacks) {
if (mKilled) {
return 0;
}
- return mInterfaces.size();
+ return mCallbacks.size();
}
}
/**
- * Return a currently registered interface. Note that this is
+ * Return a currently registered callback. Note that this is
* <em>not</em> the same as {@link #getBroadcastItem} and should not be used
- * interchangeably with it. This method returns the registered interface at the given
+ * interchangeably with it. This method returns the registered callback at the given
* index, not the current broadcast state. This means that it is not itself thread-safe:
* any call to {@link #register} or {@link #unregister} will change these indices, so you
* must do your own thread safety between these to protect from such changes.
*
- * @param index Index of which interface registration to return, from 0 to
+ * @param index Index of which callback registration to return, from 0 to
* {@link #getRegisteredCallbackCount()} - 1.
*
- * @return Returns whatever interface is associated with this index, or null if
+ * @return Returns whatever callback is associated with this index, or null if
* {@link #kill()} has been called.
*/
public E getRegisteredCallbackItem(int index) {
- synchronized (mInterfaces) {
+ synchronized (mCallbacks) {
if (mKilled) {
return null;
}
- return mInterfaces.valueAt(index).mInterface;
+ return mCallbacks.valueAt(index).mCallback;
}
}
/**
- * Return any cookie associated with a currently registered interface. Note that this is
+ * Return any cookie associated with a currently registered callback. Note that this is
* <em>not</em> the same as {@link #getBroadcastCookie} and should not be used
* interchangeably with it. This method returns the current cookie registered at the given
* index, not the current broadcast state. This means that it is not itself thread-safe:
@@ -687,25 +431,25 @@ public class RemoteCallbackList<E extends IInterface> {
* {@link #kill()} has been called.
*/
public Object getRegisteredCallbackCookie(int index) {
- synchronized (mInterfaces) {
+ synchronized (mCallbacks) {
if (mKilled) {
return null;
}
- return mInterfaces.valueAt(index).mCookie;
+ return mCallbacks.valueAt(index).mCookie;
}
}
/** @hide */
public void dump(PrintWriter pw, String prefix) {
- synchronized (mInterfaces) {
- pw.print(prefix); pw.print("callbacks: "); pw.println(mInterfaces.size());
+ synchronized (mCallbacks) {
+ pw.print(prefix); pw.print("callbacks: "); pw.println(mCallbacks.size());
pw.print(prefix); pw.print("killed: "); pw.println(mKilled);
pw.print(prefix); pw.print("broadcasts count: "); pw.println(mBroadcastCount);
}
}
- private void logExcessiveInterfaces() {
- final long size = mInterfaces.size();
+ private void logExcessiveCallbacks() {
+ final long size = mCallbacks.size();
final long TOO_MANY = 3000;
final long MAX_CHARS = 1000;
if (size >= TOO_MANY) {
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index ffc58c537f2a..61dd11fd4122 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -55,6 +55,7 @@ import java.util.Locale;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.function.BiFunction;
+import java.util.function.Function;
/**
* A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
@@ -565,6 +566,19 @@ public abstract class VibrationEffect implements Parcelable {
public abstract long getDuration();
/**
+ * Gets the estimated duration of the segment for given vibrator, in milliseconds.
+ *
+ * <p>For effects with hardware-dependent constants (e.g. primitive compositions), this returns
+ * the estimated duration based on the given {@link VibratorInfo}. For all other effects this
+ * will return the same as {@link #getDuration()}.
+ *
+ * @hide
+ */
+ public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+ return getDuration();
+ }
+
+ /**
* Checks if a vibrator with a given {@link VibratorInfo} can play this effect as intended.
*
* <p>See {@link VibratorInfo#areVibrationFeaturesSupported(VibrationEffect)} for more
@@ -904,13 +918,23 @@ public abstract class VibrationEffect implements Parcelable {
@Override
public long getDuration() {
+ return getDuration(VibrationEffectSegment::getDuration);
+ }
+
+ /** @hide */
+ @Override
+ public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+ return getDuration(segment -> segment.getDuration(vibratorInfo));
+ }
+
+ private long getDuration(Function<VibrationEffectSegment, Long> durationFn) {
if (mRepeatIndex >= 0) {
return Long.MAX_VALUE;
}
int segmentCount = mSegments.size();
long totalDuration = 0;
for (int i = 0; i < segmentCount; i++) {
- long segmentDuration = mSegments.get(i).getDuration();
+ long segmentDuration = durationFn.apply(mSegments.get(i));
if (segmentDuration < 0) {
return segmentDuration;
}
diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java
index 39f841226e4e..b17e82a704bf 100644
--- a/core/java/android/os/vibrator/PrebakedSegment.java
+++ b/core/java/android/os/vibrator/PrebakedSegment.java
@@ -16,6 +16,17 @@
package android.os.vibrator;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_THUD;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
+import static android.os.VibrationEffect.EFFECT_CLICK;
+import static android.os.VibrationEffect.EFFECT_DOUBLE_CLICK;
+import static android.os.VibrationEffect.EFFECT_HEAVY_CLICK;
+import static android.os.VibrationEffect.EFFECT_POP;
+import static android.os.VibrationEffect.EFFECT_TEXTURE_TICK;
+import static android.os.VibrationEffect.EFFECT_THUD;
+import static android.os.VibrationEffect.EFFECT_TICK;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -78,6 +89,32 @@ public final class PrebakedSegment extends VibrationEffectSegment {
/** @hide */
@Override
+ public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+ if (vibratorInfo == null) {
+ return getDuration();
+ }
+ return switch (mEffectId) {
+ case EFFECT_TICK,
+ EFFECT_CLICK,
+ EFFECT_HEAVY_CLICK -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_CLICK);
+ case EFFECT_TEXTURE_TICK -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_TICK);
+ case EFFECT_THUD -> estimateFromPrimitiveDuration(vibratorInfo, PRIMITIVE_THUD);
+ case EFFECT_DOUBLE_CLICK -> {
+ long clickDuration = vibratorInfo.getPrimitiveDuration(PRIMITIVE_CLICK);
+ yield clickDuration > 0 ? 2 * clickDuration : getDuration();
+ }
+ default -> getDuration();
+ };
+ }
+
+ private long estimateFromPrimitiveDuration(VibratorInfo vibratorInfo, int primitiveId) {
+ int duration = vibratorInfo.getPrimitiveDuration(primitiveId);
+ // Unsupported primitives should be ignored here.
+ return duration > 0 ? duration : getDuration();
+ }
+
+ /** @hide */
+ @Override
public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
if (vibratorInfo.isEffectSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) {
return true;
@@ -89,34 +126,30 @@ public final class PrebakedSegment extends VibrationEffectSegment {
}
// The vibrator does not have hardware support for the effect, but the effect has fallback
// support. Check if a fallback will be available for the effect ID.
- switch (mEffectId) {
- case VibrationEffect.EFFECT_CLICK:
- case VibrationEffect.EFFECT_DOUBLE_CLICK:
- case VibrationEffect.EFFECT_HEAVY_CLICK:
- case VibrationEffect.EFFECT_TICK:
- // Any of these effects are always supported via some form of fallback.
- return true;
- default:
- return false;
- }
+ return switch (mEffectId) {
+ // Any of these effects are always supported via some form of fallback.
+ case EFFECT_CLICK,
+ EFFECT_DOUBLE_CLICK,
+ EFFECT_HEAVY_CLICK,
+ EFFECT_TICK -> true;
+ default -> false;
+ };
}
/** @hide */
@Override
public boolean isHapticFeedbackCandidate() {
- switch (mEffectId) {
- case VibrationEffect.EFFECT_CLICK:
- case VibrationEffect.EFFECT_DOUBLE_CLICK:
- case VibrationEffect.EFFECT_HEAVY_CLICK:
- case VibrationEffect.EFFECT_POP:
- case VibrationEffect.EFFECT_TEXTURE_TICK:
- case VibrationEffect.EFFECT_THUD:
- case VibrationEffect.EFFECT_TICK:
- return true;
- default:
- // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback
- return false;
- }
+ return switch (mEffectId) {
+ case EFFECT_CLICK,
+ EFFECT_DOUBLE_CLICK,
+ EFFECT_HEAVY_CLICK,
+ EFFECT_POP,
+ EFFECT_TEXTURE_TICK,
+ EFFECT_THUD,
+ EFFECT_TICK -> true;
+ // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback
+ default -> false;
+ };
}
/** @hide */
@@ -153,27 +186,25 @@ public final class PrebakedSegment extends VibrationEffectSegment {
}
private static boolean isValidEffectStrength(int strength) {
- switch (strength) {
- case VibrationEffect.EFFECT_STRENGTH_LIGHT:
- case VibrationEffect.EFFECT_STRENGTH_MEDIUM:
- case VibrationEffect.EFFECT_STRENGTH_STRONG:
- return true;
- default:
- return false;
- }
+ return switch (strength) {
+ case VibrationEffect.EFFECT_STRENGTH_LIGHT,
+ VibrationEffect.EFFECT_STRENGTH_MEDIUM,
+ VibrationEffect.EFFECT_STRENGTH_STRONG -> true;
+ default -> false;
+ };
}
/** @hide */
@Override
public void validate() {
switch (mEffectId) {
- case VibrationEffect.EFFECT_CLICK:
- case VibrationEffect.EFFECT_DOUBLE_CLICK:
- case VibrationEffect.EFFECT_HEAVY_CLICK:
- case VibrationEffect.EFFECT_POP:
- case VibrationEffect.EFFECT_TEXTURE_TICK:
- case VibrationEffect.EFFECT_THUD:
- case VibrationEffect.EFFECT_TICK:
+ case EFFECT_CLICK:
+ case EFFECT_DOUBLE_CLICK:
+ case EFFECT_HEAVY_CLICK:
+ case EFFECT_POP:
+ case EFFECT_TEXTURE_TICK:
+ case EFFECT_THUD:
+ case EFFECT_TICK:
break;
default:
int[] ringtones = VibrationEffect.RINGTONES;
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index 3c84bcda639b..91653edd1ba5 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -77,6 +77,16 @@ public final class PrimitiveSegment extends VibrationEffectSegment {
/** @hide */
@Override
+ public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+ if (vibratorInfo == null) {
+ return getDuration();
+ }
+ int duration = vibratorInfo.getPrimitiveDuration(mPrimitiveId);
+ return duration > 0 ? duration + mDelay : getDuration();
+ }
+
+ /** @hide */
+ @Override
public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
return vibratorInfo.isPrimitiveSupported(mPrimitiveId);
}
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index e6e5a27bd731..88be96a4aef3 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -86,6 +86,7 @@ public class VibrationConfig {
private final int mDefaultKeyboardVibrationIntensity;
private final boolean mKeyboardVibrationSettingsSupported;
+ private final int mVibrationPipelineMaxDurationMs;
/** @hide */
public VibrationConfig(@Nullable Resources resources) {
@@ -106,6 +107,8 @@ public class VibrationConfig {
com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger);
mKeyboardVibrationSettingsSupported = loadBoolean(resources,
com.android.internal.R.bool.config_keyboardVibrationSettingsSupported);
+ mVibrationPipelineMaxDurationMs = loadInteger(resources,
+ com.android.internal.R.integer.config_vibrationPipelineMaxDuration, 0);
mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
@@ -221,6 +224,23 @@ public class VibrationConfig {
}
/**
+ * The max duration, in milliseconds, allowed for pipelining vibration requests.
+ *
+ * <p>If the ongoing vibration duration is shorter than this threshold then it should be allowed
+ * to finish before the next vibration can start. If the ongoing vibration is longer than this
+ * then it should be cancelled when it's superseded for the new one.
+ *
+ * @return the max duration allowed for vibration effect to finish before the next request, or
+ * zero to disable effect pipelining.
+ */
+ public int getVibrationPipelineMaxDurationMs() {
+ if (mVibrationPipelineMaxDurationMs < 0) {
+ return 0;
+ }
+ return mVibrationPipelineMaxDurationMs;
+ }
+
+ /**
* Whether or not vibrations are ignored if the device is on a wireless charger.
*
* <p>This may be the case if vibration during wireless charging causes unwanted results, like
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index e1fb4e361008..dadc849dae0a 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -17,6 +17,7 @@
package android.os.vibrator;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -58,10 +59,23 @@ public abstract class VibrationEffectSegment implements Parcelable {
*/
public abstract long getDuration();
- /**
- * Checks if a given {@link Vibrator} can play this segment as intended. See
- * {@link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information about
- * what counts as supported by a vibrator, and what counts as not.
+ /**
+ * Gets the estimated duration of the segment for given vibrator, in milliseconds.
+ *
+ * <p>For segments with hardware-dependent constants (e.g. primitives), this returns the
+ * estimated duration based on the given {@link VibratorInfo}. For all other effects this will
+ * return the same as {@link #getDuration()}.
+ *
+ * @hide
+ */
+ public long getDuration(@Nullable VibratorInfo vibratorInfo) {
+ return getDuration();
+ }
+
+ /**
+ * Checks if a given {@link android.os.Vibrator} can play this segment as intended. See
+ * {@link android.os.Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more
+ * information about what counts as supported by a vibrator, and what counts as not.
*
* @hide
*/
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index e3b1221b3004..7ceb948945fd 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -123,4 +123,25 @@ flag {
metadata {
purpose: PURPOSE_FEATURE
}
-} \ No newline at end of file
+}
+
+flag {
+ namespace: "haptics"
+ name: "primitive_composition_absolute_delay"
+ is_exported: true
+ description: "Enables functionality to create primitive compositions with absolute delays"
+ bug: "373357740"
+ metadata {
+ purpose: PURPOSE_FEATURE
+ }
+}
+
+flag {
+ namespace: "haptics"
+ name: "vibration_pipeline_enabled"
+ description: "Enables functionality to pipeline vibration effects to avoid cancelling short vibrations"
+ bug: "344494220"
+ metadata {
+ purpose: PURPOSE_FEATURE
+ }
+}
diff --git a/core/java/android/security/forensic/IForensicService.aidl b/core/java/android/security/forensic/IForensicService.aidl
new file mode 100644
index 000000000000..a944b18cb26d
--- /dev/null
+++ b/core/java/android/security/forensic/IForensicService.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright 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 android.security.forensic;
+
+import android.security.forensic.IForensicServiceCommandCallback;
+import android.security.forensic.IForensicServiceStateCallback;
+
+/**
+ * Binder interface to communicate with ForensicService.
+ * @hide
+ */
+interface IForensicService {
+ void monitorState(IForensicServiceStateCallback callback);
+ void makeVisible(IForensicServiceCommandCallback callback);
+ void makeInvisible(IForensicServiceCommandCallback callback);
+ void enable(IForensicServiceCommandCallback callback);
+ void disable(IForensicServiceCommandCallback callback);
+}
diff --git a/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl b/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl
new file mode 100644
index 000000000000..7fa0c7f72690
--- /dev/null
+++ b/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright 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 android.security.forensic;
+
+/**
+ * @hide
+ */
+ oneway interface IForensicServiceCommandCallback {
+ @Backing(type="int")
+ enum ErrorCode{
+ UNKNOWN = 0,
+ PERMISSION_DENIED = 1,
+ INVALID_STATE_TRANSITION = 2,
+ BACKUP_TRANSPORT_UNAVAILABLE = 3,
+ DATA_SOURCE_UNAVAILABLE = 3,
+ }
+ void onSuccess();
+ void onFailure(ErrorCode error);
+ }
diff --git a/core/java/android/security/forensic/IForensicServiceStateCallback.aidl b/core/java/android/security/forensic/IForensicServiceStateCallback.aidl
new file mode 100644
index 000000000000..0cda35083ffd
--- /dev/null
+++ b/core/java/android/security/forensic/IForensicServiceStateCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright 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 android.security.forensic;
+
+/**
+ * @hide
+ */
+ oneway interface IForensicServiceStateCallback {
+ @Backing(type="int")
+ enum State{
+ UNKNOWN = 0,
+ INVISIBLE = 1,
+ VISIBLE = 2,
+ ENABLED = 3,
+ }
+ void onStateChange(State state);
+ }
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 0dec13ff0c02..e254bf3e016f 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -73,8 +73,11 @@ import java.util.Locale;
public abstract class Layout {
// These should match the constants in framework/base/libs/hwui/hwui/DrawTextFunctor.h
- private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX = 4f;
- private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0.2f;
+ private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX = 0f;
+ private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0f;
+ private static final float HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_DP = 5f;
+ // since we're not using soft light yet, this needs to be much lower than the spec'd 0.8
+ private static final float HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE = 0.5f;
/** @hide */
@IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
@@ -1025,11 +1028,18 @@ public abstract class Layout {
var padding = Math.max(HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX,
mPaint.getTextSize() * HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR);
+ var cornerRadius = mPaint.density * HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_DP;
+
+ // We set the alpha on the color itself instead of Paint.setAlpha(), because that function
+ // actually mutates the color in... *ehem* very strange ways. Also the color might get reset
+ // for various reasons, which also resets the alpha.
+ var white = Color.argb(HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE, 1f, 1f, 1f);
+ var black = Color.argb(HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE, 0f, 0f, 0f);
var originalTextColor = mPaint.getColor();
var bgPaint = mWorkPlainPaint;
bgPaint.reset();
- bgPaint.setColor(isHighContrastTextDark(originalTextColor) ? Color.WHITE : Color.BLACK);
+ bgPaint.setColor(isHighContrastTextDark(originalTextColor) ? white : black);
bgPaint.setStyle(Paint.Style.FILL);
int start = getLineStart(firstLine);
@@ -1082,7 +1092,12 @@ public abstract class Layout {
private void drawRect() {
if (!mLineBackground.isEmpty()) {
mLineBackground.inset(-padding, -padding);
- canvas.drawRect(mLineBackground, bgPaint);
+ canvas.drawRoundRect(
+ mLineBackground,
+ cornerRadius,
+ cornerRadius,
+ bgPaint
+ );
}
}
@@ -1104,7 +1119,7 @@ public abstract class Layout {
if (hasColorChanged) {
mLastColor = textColor;
- return isHighContrastTextDark(textColor) ? Color.WHITE : Color.BLACK;
+ return isHighContrastTextDark(textColor) ? white : black;
}
return bgPaint.getColor();
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 9f54d9fca24b..3adbd686cd2c 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -24,7 +24,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.Network;
-import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
import android.net.SntpClient;
import android.os.Build;
import android.os.SystemClock;
@@ -687,16 +687,8 @@ public abstract class NtpTrustedTime implements TrustedTime {
if (connectivityManager == null) {
return false;
}
- final NetworkCapabilities networkCapabilities =
- connectivityManager.getNetworkCapabilities(network);
- if (networkCapabilities == null) {
- if (LOGD) Log.d(TAG, "getNetwork: failed to get network capabilities");
- return false;
- }
- final boolean isConnectedToInternet = networkCapabilities.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_INTERNET)
- && networkCapabilities.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ final NetworkInfo ni = connectivityManager.getNetworkInfo(network);
+
// This connectivity check is to avoid performing a DNS lookup for the time server on a
// unconnected network. There are races to obtain time in Android when connectivity
// changes, which means that forceRefresh() can be called by various components before
@@ -706,8 +698,8 @@ public abstract class NtpTrustedTime implements TrustedTime {
// A side effect of check is that tests that run a fake NTP server on the device itself
// will only be able to use it if the active network is connected, even though loopback
// addresses are actually reachable.
- if (!isConnectedToInternet) {
- if (LOGD) Log.d(TAG, "getNetwork: no internet connectivity");
+ if (ni == null || !ni.isConnected()) {
+ if (LOGD) Log.d(TAG, "getNetwork: no connectivity");
return false;
}
return true;
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index d08873c56e6a..59c66532fe0b 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -22,6 +22,7 @@ import static android.view.InsetsControllerProto.CONTROL;
import static android.view.InsetsControllerProto.STATE;
import static android.view.InsetsSource.ID_IME;
import static android.view.InsetsSource.ID_IME_CAPTION_BAR;
+import static android.view.ViewProtoLogGroups.IME_INSETS_CONTROLLER;
import static android.view.WindowInsets.Type.FIRST;
import static android.view.WindowInsets.Type.LAST;
import static android.view.WindowInsets.Type.all;
@@ -69,6 +70,7 @@ import android.view.inputmethod.InputMethodManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.function.TriFunction;
import java.io.PrintWriter;
@@ -1920,6 +1922,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
final @InsetsType int requestedVisibleTypes =
(mRequestedVisibleTypes & ~mask) | (visibleTypes & mask);
if (mRequestedVisibleTypes != requestedVisibleTypes) {
+ ProtoLog.d(IME_INSETS_CONTROLLER, "Setting requestedVisibleTypes to %d (was %d)",
+ requestedVisibleTypes, mRequestedVisibleTypes);
mRequestedVisibleTypes = requestedVisibleTypes;
}
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 83b4971c8621..eb59e21cd7e2 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1734,9 +1734,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mRTLastReportedPosition.top /*positionTop*/,
postScaleX, postScaleY);
- mRTLastSetCrop.set((int) (clipLeft / postScaleX), (int) (clipTop / postScaleY),
- (int) Math.ceil(clipRight / postScaleX),
- (int) Math.ceil(clipBottom / postScaleY));
+ mRTLastSetCrop.set(clipLeft, clipTop, clipRight, clipBottom);
if (DEBUG_POSITION) {
Log.d(TAG, String.format("Setting layer crop = [%d, %d, %d, %d] "
+ "from scale %f, %f", mRTLastSetCrop.left,
diff --git a/core/java/android/view/ViewProtoLogGroups.java b/core/java/android/view/ViewProtoLogGroups.java
new file mode 100644
index 000000000000..099f76189a50
--- /dev/null
+++ b/core/java/android/view/ViewProtoLogGroups.java
@@ -0,0 +1,42 @@
+/*
+ * 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 android.view;
+
+import android.annotation.NonNull;
+import android.view.inputmethod.Flags;
+
+import com.android.internal.protolog.ProtoLogGroup;
+
+import java.util.UUID;
+
+/**
+ * Defines logging groups for ProtoLog.
+ *
+ * This file is used by the ProtoLogTool to generate optimized logging code. All of its dependencies
+ * must be included in services.core.wm.protologgroups build target.
+ *
+ * @hide
+ */
+final class ViewProtoLogGroups {
+ final static ProtoLogGroup IME_INSETS_CONTROLLER = new ProtoLogGroup(
+ "IME_INSETS_CONTROLLER", "InsetsController", Flags.refactorInsetsController());
+
+ final static ProtoLogGroup[] ALL_GROUPS = {
+ IME_INSETS_CONTROLLER
+ };
+}
+
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3be9a821a463..1f17e8ec7b85 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -279,6 +279,7 @@ import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneFallbackEventHandler;
+import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
@@ -1282,6 +1283,8 @@ public final class ViewRootImpl implements ViewParent,
mIsStylusPointerIconEnabled =
InputSettings.isStylusPointerIconEnabled(mContext);
+ initializeProtoLogInProcess();
+
String processorOverrideName = context.getResources().getString(
R.string.config_inputEventCompatProcessorOverrideClassName);
if (processorOverrideName.isEmpty()) {
@@ -5949,7 +5952,34 @@ public final class ViewRootImpl implements ViewParent,
// If no intersection, set bounds to empty.
bounds.setEmpty();
}
- return !bounds.isEmpty();
+
+ if (bounds.isEmpty()) {
+ return false;
+ }
+
+ if (android.view.accessibility.Flags.focusRectMinSize()) {
+ adjustAccessibilityFocusedRectBoundsIfNeeded(bounds);
+ }
+
+ return true;
+ }
+
+ /**
+ * Adjusts accessibility focused rect bounds so that they are not invisible.
+ *
+ * <p>Focus bounds smaller than double the stroke width are very hard to see (or invisible).
+ * Expand the focus bounds if necessary to at least double the stroke width.
+ * @param bounds The bounds to adjust
+ */
+ @VisibleForTesting
+ public void adjustAccessibilityFocusedRectBoundsIfNeeded(Rect bounds) {
+ final int minRectLength = mAccessibilityManager.getAccessibilityFocusStrokeWidth() * 2;
+ if (bounds.width() < minRectLength || bounds.height() < minRectLength) {
+ final float missingWidth = Math.max(0, minRectLength - bounds.width());
+ final float missingHeight = Math.max(0, minRectLength - bounds.height());
+ bounds.inset(-1 * (int) Math.ceil(missingWidth / 2),
+ -1 * (int) Math.ceil(missingHeight / 2));
+ }
}
private Drawable getAccessibilityFocusedDrawable() {
@@ -13403,4 +13433,13 @@ public final class ViewRootImpl implements ViewParent,
mCurrentColorMode = colorMode;
}
+
+ private static boolean sProtoLogInitialized = false;
+
+ private void initializeProtoLogInProcess() {
+ if (!sProtoLogInitialized) {
+ ProtoLog.init(ViewProtoLogGroups.ALL_GROUPS);
+ sProtoLogInitialized = true;
+ }
+ }
}
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 8ffae845de1f..820a1fba11ad 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -96,6 +96,16 @@ flag {
flag {
namespace: "accessibility"
+ name: "focus_rect_min_size"
+ description: "Ensures the a11y focus rect is big enough to be drawn as visible"
+ bug: "368667566"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "accessibility"
name: "force_invert_color"
description: "Enable force force-dark for smart inversion and dark theme everywhere"
bug: "282821643"
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 1e5c6d8177e1..47fc43735c4d 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2352,6 +2352,13 @@ public final class InputMethodManager {
* {@link #RESULT_HIDDEN}.
* @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
* this does not return result of the request. For result use {@param resultReceiver} instead.
+ *
+ * @deprecated The {@link ResultReceiver} is not a reliable way of determining whether the
+ * Input Method is actually shown or hidden. If result is needed, use
+ * {@link android.view.WindowInsetsController#show} instead and set a
+ * {@link View.OnApplyWindowInsetsListener} and verify the provided {@link WindowInsets} for
+ * the visibility of IME. If result is not needed, use {@link #showSoftInput(View, int)}
+ * instead.
*/
public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) {
return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT);
@@ -2399,6 +2406,14 @@ public final class InputMethodManager {
& WindowInsets.Type.ime()) == 0) {
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_NO_ONGOING_USER_ANIMATION);
+ if (resultReceiver != null) {
+ final boolean imeReqVisible =
+ (viewRootImpl.getInsetsController().getRequestedVisibleTypes()
+ & WindowInsets.Type.ime()) != 0;
+ resultReceiver.send(
+ imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_SHOWN
+ : InputMethodManager.RESULT_SHOWN, null);
+ }
// TODO(b/322992891) handle case of SHOW_IMPLICIT
viewRootImpl.getInsetsController().show(WindowInsets.Type.ime(),
false /* fromIme */, statsToken);
@@ -2531,6 +2546,13 @@ public final class InputMethodManager {
* {@link #RESULT_HIDDEN}.
* @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
* this does not return result of the request. For result use {@param resultReceiver} instead.
+ *
+ * @deprecated The {@link ResultReceiver} is not a reliable way of determining whether the
+ * Input Method is actually shown or hidden. If result is needed, use
+ * {@link android.view.WindowInsetsController#hide} instead and set a
+ * {@link View.OnApplyWindowInsetsListener} and verify the provided {@link WindowInsets} for
+ * the visibility of IME. If result is not needed, use
+ * {@link #hideSoftInputFromView(View, int)} instead.
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
ResultReceiver resultReceiver) {
@@ -2569,6 +2591,14 @@ public final class InputMethodManager {
// TODO(b/322992891) handle case of HIDE_IMPLICIT_ONLY
final var viewRootImpl = servedView.getViewRootImpl();
if (viewRootImpl != null) {
+ if (resultReceiver != null) {
+ final boolean imeReqVisible =
+ (viewRootImpl.getInsetsController().getRequestedVisibleTypes()
+ & WindowInsets.Type.ime()) != 0;
+ resultReceiver.send(
+ !imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_HIDDEN
+ : InputMethodManager.RESULT_HIDDEN, null);
+ }
viewRootImpl.getInsetsController().hide(WindowInsets.Type.ime());
}
return true;
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index bae8affcec47..aa4927ee9b9c 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -73,6 +73,17 @@ flag {
}
flag {
+ name: "consistent_get_current_input_method_info"
+ namespace: "input_method"
+ description: "Use BindingController as the source of truth in getCurrentInputMethodInfo"
+ bug: "355034523"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "ime_switcher_revamp"
is_exported: true
namespace: "input_method"
diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java
index 1c3f201c1471..23e572fcd577 100644
--- a/core/java/android/window/BackEvent.java
+++ b/core/java/android/window/BackEvent.java
@@ -156,6 +156,22 @@ public final class BackEvent {
}
@Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof BackEvent)) {
+ return false;
+ }
+ final BackEvent that = (BackEvent) other;
+ return mTouchX == that.mTouchX
+ && mTouchY == that.mTouchY
+ && mProgress == that.mProgress
+ && mSwipeEdge == that.mSwipeEdge
+ && mFrameTime == that.mFrameTime;
+ }
+
+ @Override
public String toString() {
return "BackEvent{"
+ "mTouchX=" + mTouchX
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 8e35843e2193..05dc910b89de 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -64,7 +64,9 @@ public enum DesktopModeFlags {
ENABLE_WINDOWING_EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS(
Flags::enableDesktopWindowingTaskbarRunningApps, true),
+ // TODO: b/369763947 - remove this once ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS is ramped up
ENABLE_DESKTOP_WINDOWING_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false),
+ ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false),
ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS(Flags::enableDesktopWindowingExitTransitions, false),
ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
Flags::enableWindowingTransitionHandlersObservers, false);
diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java
index 20d1b3bd12ae..a37bef80ff04 100644
--- a/core/java/android/window/TaskSnapshot.java
+++ b/core/java/android/window/TaskSnapshot.java
@@ -83,13 +83,16 @@ public class TaskSnapshot implements Parcelable {
public static final int REFERENCE_CACHE = 1 << 1;
/** This snapshot object is being persistent. */
public static final int REFERENCE_PERSIST = 1 << 2;
+ /** This snapshot object is being used for content suggestion. */
+ public static final int REFERENCE_CONTENT_SUGGESTION = 1 << 3;
@IntDef(flag = true, prefix = { "REFERENCE_" }, value = {
REFERENCE_BROADCAST,
REFERENCE_CACHE,
- REFERENCE_PERSIST
+ REFERENCE_PERSIST,
+ REFERENCE_CONTENT_SUGGESTION
})
@Retention(RetentionPolicy.SOURCE)
- @interface ReferenceFlags {}
+ public @interface ReferenceFlags {}
public TaskSnapshot(long id, long captureTime,
@NonNull ComponentName topActivityComponent, HardwareBuffer snapshot,
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 8e495ec1dc40..34abf3114925 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -525,6 +525,22 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Disables or enables activities to be started in adjacent tasks (see
+ * {@link FLAG_ACTIVITY_LAUNCH_ADJACENT}) for the specified root of any child tasks. This
+ * differs from {@link #setLaunchAdjacentFlagRoot(WindowContainerToken)} which controls the
+ * preferred launch-adjacent target and allows for selectively setting which root tasks can
+ * support launch-adjacent.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setDisableLaunchAdjacent(
+ @NonNull WindowContainerToken container, boolean disabled) {
+ mHierarchyOps.add(HierarchyOp.createForSetDisableLaunchAdjacent(container.asBinder(),
+ disabled));
+ return this;
+ }
+
+ /**
* Starts a task by id. The task is expected to already exist (eg. as a recent task).
* @param taskId Id of task to start.
* @param options bundle containing ActivityOptions for the task's top activity.
@@ -1488,6 +1504,7 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION = 20;
public static final int HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES = 21;
public static final int HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE = 22;
+ public static final int HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT = 23;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.
@@ -1556,6 +1573,8 @@ public final class WindowContainerTransaction implements Parcelable {
private @InsetsType int mExcludeInsetsTypes;
+ private boolean mLaunchAdjacentDisabled;
+
public static HierarchyOp createForReparent(
@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
@@ -1644,6 +1663,15 @@ public final class WindowContainerTransaction implements Parcelable {
.build();
}
+ /** Create a hierarchy op for disabling launch adjacent. */
+ public static HierarchyOp createForSetDisableLaunchAdjacent(IBinder container,
+ boolean disabled) {
+ return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT)
+ .setContainer(container)
+ .setLaunchAdjacentDisabled(disabled)
+ .build();
+ }
+
/** create a hierarchy op for deleting a task **/
public static HierarchyOp createForRemoveTask(@NonNull IBinder container) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REMOVE_TASK)
@@ -1695,6 +1723,7 @@ public final class WindowContainerTransaction implements Parcelable {
mReparentLeafTaskIfRelaunch = copy.mReparentLeafTaskIfRelaunch;
mIsTrimmableFromRecents = copy.mIsTrimmableFromRecents;
mExcludeInsetsTypes = copy.mExcludeInsetsTypes;
+ mLaunchAdjacentDisabled = copy.mLaunchAdjacentDisabled;
}
protected HierarchyOp(Parcel in) {
@@ -1719,6 +1748,7 @@ public final class WindowContainerTransaction implements Parcelable {
mReparentLeafTaskIfRelaunch = in.readBoolean();
mIsTrimmableFromRecents = in.readBoolean();
mExcludeInsetsTypes = in.readInt();
+ mLaunchAdjacentDisabled = in.readBoolean();
}
public int getType() {
@@ -1814,13 +1844,11 @@ public final class WindowContainerTransaction implements Parcelable {
}
/** Denotes whether the parents should also be included in the op. */
- @NonNull
public boolean includingParents() {
return mIncludingParents;
}
- /** Set the task to be trimmable */
- @NonNull
+ /** Denotes whether the task can be trimmable from recents */
public boolean isTrimmableFromRecents() {
return mIsTrimmableFromRecents;
}
@@ -1829,6 +1857,11 @@ public final class WindowContainerTransaction implements Parcelable {
return mExcludeInsetsTypes;
}
+ /** Denotes whether launch-adjacent flag is respected from this task or its children */
+ public boolean isLaunchAdjacentDisabled() {
+ return mLaunchAdjacentDisabled;
+ }
+
/** Gets a string representation of a hierarchy-op type. */
public static String hopToString(int type) {
switch (type) {
@@ -1839,6 +1872,8 @@ public final class WindowContainerTransaction implements Parcelable {
case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "SetAdjacentRoot";
case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "LaunchTask";
case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "SetAdjacentFlagRoot";
+ case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT:
+ return "SetDisableLaunchAdjacent";
case HIERARCHY_OP_TYPE_PENDING_INTENT: return "PendingIntent";
case HIERARCHY_OP_TYPE_START_SHORTCUT: return "StartShortcut";
case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: return "addInsetsFrameProvider";
@@ -1891,6 +1926,10 @@ public final class WindowContainerTransaction implements Parcelable {
case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
sb.append("container=").append(mContainer).append(" clearRoot=").append(mToTop);
break;
+ case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT:
+ sb.append("container=").append(mContainer).append(" disabled=")
+ .append(mLaunchAdjacentDisabled);
+ break;
case HIERARCHY_OP_TYPE_START_SHORTCUT:
sb.append("options=").append(mLaunchOptions)
.append(" info=").append(mShortcutInfo);
@@ -1971,6 +2010,7 @@ public final class WindowContainerTransaction implements Parcelable {
dest.writeBoolean(mReparentLeafTaskIfRelaunch);
dest.writeBoolean(mIsTrimmableFromRecents);
dest.writeInt(mExcludeInsetsTypes);
+ dest.writeBoolean(mLaunchAdjacentDisabled);
}
@Override
@@ -2047,6 +2087,8 @@ public final class WindowContainerTransaction implements Parcelable {
private @InsetsType int mExcludeInsetsTypes;
+ private boolean mLaunchAdjacentDisabled;
+
Builder(int type) {
mType = type;
}
@@ -2153,6 +2195,11 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
+ Builder setLaunchAdjacentDisabled(boolean disabled) {
+ mLaunchAdjacentDisabled = disabled;
+ return this;
+ }
+
HierarchyOp build() {
final HierarchyOp hierarchyOp = new HierarchyOp(mType);
hierarchyOp.mContainer = mContainer;
@@ -2179,6 +2226,7 @@ public final class WindowContainerTransaction implements Parcelable {
hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch;
hierarchyOp.mIsTrimmableFromRecents = mIsTrimmableFromRecents;
hierarchyOp.mExcludeInsetsTypes = mExcludeInsetsTypes;
+ hierarchyOp.mLaunchAdjacentDisabled = mLaunchAdjacentDisabled;
return hierarchyOp;
}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 44a374fb7c20..c9d458f22463 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -17,6 +17,7 @@
package android.window;
import static com.android.window.flags.Flags.predictiveBackPrioritySystemNavigationObserver;
+import static com.android.window.flags.Flags.predictiveBackTimestampApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -563,7 +564,8 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
}
OnBackAnimationCallback animationCallback = getBackAnimationCallback();
if (animationCallback != null
- && !(callback instanceof ImeBackAnimationController)) {
+ && !(callback instanceof ImeBackAnimationController)
+ && !predictiveBackTimestampApi()) {
mProgressAnimator.onBackInvoked(() -> {
if (mIsSystemCallback) {
mSystemNavigationObserverCallbackRunnable.run();
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 155494fb3b25..45f6480b0b7f 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -239,6 +239,13 @@ flag {
}
flag {
+ name: "enable_desktop_windowing_enter_transitions"
+ namespace: "lse_desktop_experience"
+ description: "Enables enter desktop windowing transition & motion polish changes"
+ bug: "369763947"
+}
+
+flag {
name: "enable_desktop_windowing_exit_transitions"
namespace: "lse_desktop_experience"
description: "Enables exit desktop windowing transition & motion polish changes"
@@ -301,6 +308,13 @@ flag {
}
flag {
+ name: "enable_restore_to_previous_size_from_desktop_immersive"
+ namespace: "lse_desktop_experience"
+ description: "Restores the window bounds to their previous size when exiting desktop immersive"
+ bug: "372318163"
+}
+
+flag {
name: "enable_display_focus_in_shell_transitions"
namespace: "lse_desktop_experience"
description: "Creates a shell transition when display focus switches."
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 18c8eb4ec46b..de7ad346a7cd 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -1195,7 +1195,12 @@ public class ResolverListAdapter extends BaseAdapter {
@Nullable
protected Drawable loadIconFromResource(Resources res, int resId) {
- return res.getDrawableForDensity(resId, mIconDpi);
+ try {
+ return res.getDrawableForDensity(resId, mIconDpi);
+ } catch (Resources.NotFoundException e) {
+ Log.e(TAG, "Resource not found", e);
+ return null;
+ }
}
}
diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index 0a80e006d5bc..3c201fc42df3 100644
--- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -39,7 +39,7 @@ public class ProtoLogViewerConfigReader {
* or the viewer config is not loaded into memory.
*/
@Nullable
- public synchronized String getViewerString(long messageHash) {
+ public String getViewerString(long messageHash) {
return mLogMessageMap.get(messageHash);
}
@@ -179,6 +179,6 @@ public class ProtoLogViewerConfigReader {
}
}
- throw new RuntimeException("Group " + group + "not found in viewer config");
+ throw new RuntimeException("Group " + group + " not found in viewer config");
}
}
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index 3e597d73f217..d3b1f972a955 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -20,16 +20,20 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification.ProgressStyle;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.graphics.drawable.LayerDrawable;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.RemotableViewMethod;
import android.widget.ProgressBar;
import android.widget.RemoteViews;
import androidx.annotation.ColorInt;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.NotificationProgressDrawable.Part;
@@ -49,7 +53,13 @@ import java.util.TreeSet;
*/
@RemoteViews.RemoteView
public class NotificationProgressBar extends ProgressBar {
+ private static final String TAG = "NotificationProgressBar";
+
private NotificationProgressModel mProgressModel;
+
+ @Nullable
+ private List<Part> mProgressDrawableParts = null;
+
@Nullable
private Drawable mProgressTrackerDrawable = null;
@@ -58,7 +68,7 @@ public class NotificationProgressBar extends ProgressBar {
}
public NotificationProgressBar(Context context, AttributeSet attrs) {
- this(context, attrs, com.android.internal.R.attr.progressBarStyle);
+ this(context, attrs, R.attr.progressBarStyle);
}
public NotificationProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -82,10 +92,42 @@ public class NotificationProgressBar extends ProgressBar {
"Bundle shouldn't be null");
mProgressModel = NotificationProgressModel.fromBundle(bundle);
+
+ if (mProgressModel.isIndeterminate()) {
+ final int indeterminateColor = mProgressModel.getIndeterminateColor();
+ setIndeterminateTintList(ColorStateList.valueOf(indeterminateColor));
+ } else {
+ mProgressDrawableParts = processAndConvertToDrawableParts(mProgressModel.getSegments(),
+ mProgressModel.getPoints(),
+ mProgressModel.getProgress(), mProgressModel.isStyledByProgress());
+
+ try {
+ final NotificationProgressDrawable drawable = getNotificationProgressDrawable();
+ drawable.setParts(mProgressDrawableParts);
+ } catch (IllegalStateException ex) {
+ Log.e(TAG, "Can't set parts because can't get NotificationProgressDrawable", ex);
+ }
+ }
}
- private void setProgressModel(@NonNull NotificationProgressModel model) {
- mProgressModel = model;
+ @NonNull
+ private NotificationProgressDrawable getNotificationProgressDrawable() {
+ final Drawable d = getProgressDrawable();
+ if (d == null) {
+ throw new IllegalStateException("getProgressDrawable() returns null");
+ }
+ if (!(d instanceof LayerDrawable)) {
+ throw new IllegalStateException("getProgressDrawable() doesn't return a LayerDrawable");
+ }
+
+ final Drawable layer = ((LayerDrawable) d).findDrawableByLayerId(R.id.background);
+ if (!(layer instanceof NotificationProgressDrawable)) {
+ throw new IllegalStateException(
+ "Couldn't get NotificationProgressDrawable, retrieved drawable is: " + (
+ layer != null ? layer.toString() : null));
+ }
+
+ return (NotificationProgressDrawable) layer;
}
/**
@@ -97,7 +139,6 @@ public class NotificationProgressBar extends ProgressBar {
public void setProgressTrackerIcon(@Nullable Icon icon) {
}
-
/**
* Async version of {@link #setProgressTrackerIcon}
*/
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index 89ef8759a169..2be7273e5d10 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -45,6 +45,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
/**
@@ -156,11 +157,18 @@ public final class NotificationProgressDrawable extends Drawable {
}
/**
- *
+ * Set the segments and points that constitute the drawable.
*/
- public void setParts(@NonNull Part... parts) {
+ public void setParts(List<Part> parts) {
mParts.clear();
- mParts.addAll(Arrays.asList(parts));
+ mParts.addAll(parts);
+ }
+
+ /**
+ * Set the segments and points that constitute the drawable.
+ */
+ public void setParts(@NonNull Part... parts) {
+ setParts(Arrays.asList(parts));
}
@Override
@@ -379,7 +387,7 @@ public final class NotificationProgressDrawable extends Drawable {
if (state.mThemeAttrsPoints != null) {
final TypedArray a = t.resolveAttributes(
state.mThemeAttrsPoints, R.styleable.NotificationProgressDrawablePoints);
- updateSegmentsFromTypedArray(a);
+ updatePointsFromTypedArray(a);
a.recycle();
}
}
@@ -651,9 +659,11 @@ public final class NotificationProgressDrawable extends Drawable {
State(@NonNull State orig, @Nullable Resources res) {
mChangingConfigurations = orig.mChangingConfigurations;
+ mSegSegGap = orig.mSegSegGap;
+ mSegPointGap = orig.mSegPointGap;
+ mStrokeWidth = orig.mStrokeWidth;
mStrokeColor = orig.mStrokeColor;
mFadedStrokeColor = orig.mFadedStrokeColor;
- mStrokeWidth = orig.mStrokeWidth;
mStrokeDashWidth = orig.mStrokeDashWidth;
mStrokeDashGap = orig.mStrokeDashGap;
mPointRadius = orig.mPointRadius;
@@ -791,6 +801,7 @@ public final class NotificationProgressDrawable extends Drawable {
final State state = mState;
mStrokePaint.setStrokeWidth(state.mStrokeWidth);
+ mDashedStrokePaint.setStrokeWidth(state.mStrokeWidth);
if (state.mStrokeDashWidth != 0.0f) {
final DashPathEffect e = new DashPathEffect(
diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java
index 98e6e8505534..adcc0f64b598 100644
--- a/core/java/com/android/internal/widget/NotificationRowIconView.java
+++ b/core/java/com/android/internal/widget/NotificationRowIconView.java
@@ -39,17 +39,24 @@ import com.android.internal.R;
/**
* An image view that holds the icon displayed at the start of a notification row.
+ * This can generally either display the "small icon" of a notification set via
+ * {@link this#setImageIcon(Icon)}, or an app icon controlled and fetched by the provider set
+ * through {@link this#setIconProvider(NotificationIconProvider)}.
*/
@RemoteViews.RemoteView
public class NotificationRowIconView extends CachingIconView {
+ private NotificationIconProvider mIconProvider;
+
private boolean mApplyCircularCrop = false;
private boolean mShouldShowAppIcon = false;
+ private Drawable mAppIcon = null;
- // Padding and background set on the view prior to being changed by setShouldShowAppIcon(true),
- // to be restored if shouldShowAppIcon becomes false again.
+ // Padding, background and colors set on the view prior to being overridden when showing the app
+ // icon, to be restored if we're showing the small icon again.
private Rect mOriginalPadding = null;
private Drawable mOriginalBackground = null;
-
+ private int mOriginalBackgroundColor = ColoredIconHelper.COLOR_INVALID;
+ private int mOriginalIconColor = ColoredIconHelper.COLOR_INVALID;
public NotificationRowIconView(Context context) {
super(context);
@@ -81,6 +88,71 @@ public class NotificationRowIconView extends CachingIconView {
super.onFinishInflate();
}
+ /**
+ * Sets the icon provider for this view. This is used to determine whether we should show the
+ * app icon instead of the small icon, and to fetch the app icon if needed.
+ */
+ public void setIconProvider(NotificationIconProvider iconProvider) {
+ mIconProvider = iconProvider;
+ }
+
+ private Drawable loadAppIcon() {
+ if (mIconProvider != null && mIconProvider.shouldShowAppIcon()) {
+ return mIconProvider.getAppIcon();
+ }
+ return null;
+ }
+
+ @RemotableViewMethod(asyncImpl = "setImageIconAsync")
+ @Override
+ public void setImageIcon(Icon icon) {
+ if (Flags.notificationsRedesignAppIcons()) {
+ if (mAppIcon != null) {
+ // We already know that we should be using the app icon, and we already loaded it.
+ // We assume that cannot change throughout the lifetime of a notification, so
+ // there's nothing to do here.
+ return;
+ }
+ mAppIcon = loadAppIcon();
+ if (mAppIcon != null) {
+ setImageDrawable(mAppIcon);
+ adjustViewForAppIcon();
+ } else {
+ super.setImageIcon(icon);
+ restoreViewForSmallIcon();
+ }
+ return;
+ }
+ super.setImageIcon(icon);
+ }
+
+ @RemotableViewMethod
+ @Override
+ public Runnable setImageIconAsync(Icon icon) {
+ if (Flags.notificationsRedesignAppIcons()) {
+ if (mAppIcon != null) {
+ // We already know that we should be using the app icon, and we already loaded it.
+ // We assume that cannot change throughout the lifetime of a notification, so
+ // there's nothing to do here.
+ return () -> {
+ };
+ }
+ mAppIcon = loadAppIcon();
+ if (mAppIcon != null) {
+ return () -> {
+ setImageDrawable(mAppIcon);
+ adjustViewForAppIcon();
+ };
+ } else {
+ return () -> {
+ super.setImageIcon(icon);
+ restoreViewForSmallIcon();
+ };
+ }
+ }
+ return super.setImageIconAsync(icon);
+ }
+
/** Whether the icon represents the app icon (instead of the small icon). */
@RemotableViewMethod
public void setShouldShowAppIcon(boolean shouldShowAppIcon) {
@@ -91,35 +163,122 @@ public class NotificationRowIconView extends CachingIconView {
mShouldShowAppIcon = shouldShowAppIcon;
if (mShouldShowAppIcon) {
- if (mOriginalPadding == null && mOriginalBackground == null) {
- mOriginalPadding = new Rect(getPaddingLeft(), getPaddingTop(),
- getPaddingRight(), getPaddingBottom());
- mOriginalBackground = getBackground();
- }
-
- setPadding(0, 0, 0, 0);
-
- // Make the background white in case the icon itself doesn't have one.
- ColorFilter colorFilter = new PorterDuffColorFilter(Color.WHITE,
- PorterDuff.Mode.SRC_ATOP);
-
- if (mOriginalBackground == null) {
- setBackground(getContext().getDrawable(R.drawable.notification_icon_circle));
- }
- getBackground().mutate().setColorFilter(colorFilter);
+ adjustViewForAppIcon();
} else {
// Restore original padding and background if needed
- if (mOriginalPadding != null) {
- setPadding(mOriginalPadding.left, mOriginalPadding.top, mOriginalPadding.right,
- mOriginalPadding.bottom);
- mOriginalPadding = null;
- }
- setBackground(mOriginalBackground);
- mOriginalBackground = null;
+ restoreViewForSmallIcon();
}
}
}
+ /**
+ * Override padding and background from the view to display the app icon.
+ */
+ private void adjustViewForAppIcon() {
+ removePadding();
+
+ if (Flags.notificationsUseAppIconInRow()) {
+ addWhiteBackground();
+ } else {
+ // No need to set the background for notification redesign, since the icon
+ // factory already does that for us.
+ removeBackground();
+ }
+ }
+
+ /**
+ * Restore padding and background overridden by {@link this#adjustViewForAppIcon}.
+ * Does nothing if they were not overridden.
+ */
+ private void restoreViewForSmallIcon() {
+ restorePadding();
+ restoreBackground();
+ restoreColors();
+ }
+
+ private void removePadding() {
+ if (mOriginalPadding == null) {
+ mOriginalPadding = new Rect(getPaddingLeft(), getPaddingTop(),
+ getPaddingRight(), getPaddingBottom());
+ }
+ setPadding(0, 0, 0, 0);
+ }
+
+ private void restorePadding() {
+ if (mOriginalPadding != null) {
+ setPadding(mOriginalPadding.left, mOriginalPadding.top,
+ mOriginalPadding.right,
+ mOriginalPadding.bottom);
+ mOriginalPadding = null;
+ }
+ }
+
+ private void removeBackground() {
+ if (mOriginalBackground == null) {
+ mOriginalBackground = getBackground();
+ }
+
+ setBackground(null);
+ }
+
+ private void addWhiteBackground() {
+ if (mOriginalBackground == null) {
+ mOriginalBackground = getBackground();
+ }
+
+ // Make the background white in case the icon itself doesn't have one.
+ ColorFilter colorFilter = new PorterDuffColorFilter(Color.WHITE,
+ PorterDuff.Mode.SRC_ATOP);
+
+ if (mOriginalBackground == null) {
+ setBackground(getContext().getDrawable(R.drawable.notification_icon_circle));
+ }
+ getBackground().mutate().setColorFilter(colorFilter);
+ }
+
+ private void restoreBackground() {
+ // NOTE: This will not work if the original background was null, but that's better than
+ // accidentally clearing the background. We expect that there's generally going to be one
+ // anyway unless we manually clear it.
+ if (mOriginalBackground != null) {
+ setBackground(mOriginalBackground);
+ mOriginalBackground = null;
+ }
+ }
+
+ private void restoreColors() {
+ if (mOriginalBackgroundColor != ColoredIconHelper.COLOR_INVALID) {
+ super.setBackgroundColor(mOriginalBackgroundColor);
+ mOriginalBackgroundColor = ColoredIconHelper.COLOR_INVALID;
+ }
+ if (mOriginalIconColor != ColoredIconHelper.COLOR_INVALID) {
+ super.setOriginalIconColor(mOriginalIconColor);
+ mOriginalIconColor = ColoredIconHelper.COLOR_INVALID;
+ }
+ }
+
+ @RemotableViewMethod
+ @Override
+ public void setBackgroundColor(int color) {
+ // Ignore color overrides if we're showing the app icon.
+ if (mAppIcon == null) {
+ super.setBackgroundColor(color);
+ } else {
+ mOriginalBackgroundColor = color;
+ }
+ }
+
+ @RemotableViewMethod
+ @Override
+ public void setOriginalIconColor(int color) {
+ // Ignore color overrides if we're showing the app icon.
+ if (mAppIcon == null) {
+ super.setOriginalIconColor(color);
+ } else {
+ mOriginalIconColor = color;
+ }
+ }
+
@Nullable
@Override
Drawable loadSizeRestrictedIcon(@Nullable Icon icon) {
@@ -197,4 +356,17 @@ public class NotificationRowIconView extends CachingIconView {
return bitmap;
}
+
+ /**
+ * A provider that allows this view to verify whether it should use the app icon instead of the
+ * icon provided to it via setImageIcon, as well as actually fetching the app icon. It should
+ * primarily be called on the background thread.
+ */
+ public interface NotificationIconProvider {
+ /** Whether this notification should use the app icon instead of the small icon. */
+ boolean shouldShowAppIcon();
+
+ /** Get the app icon for this notification. */
+ Drawable getAppIcon();
+ }
}
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 68d49cda191d..041fed74c573 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -336,7 +336,7 @@ static void CameraMetadata_swap(JNIEnv *env, jclass thiz, jlong ptr, jlong other
static jbyteArray CameraMetadata_readValues(JNIEnv *env, jclass thiz, jint tag, jlong ptr) {
ALOGV("%s (tag = %d)", __FUNCTION__, tag);
- const CameraMetadata *metadata = CameraMetadata_getPointerThrow(env, ptr);
+ CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, ptr);
if (metadata == NULL) return NULL;
const camera_metadata_t *metaBuffer = metadata->getAndLock();
@@ -349,14 +349,16 @@ static jbyteArray CameraMetadata_readValues(JNIEnv *env, jclass thiz, jint tag,
}
size_t tagSize = Helpers::getTypeSize(tagType);
- camera_metadata_ro_entry entry = metadata->find(tag);
+ camera_metadata_entry entry = metadata->find(tag);
if (entry.count == 0) {
- if (!metadata->exists(tag)) {
- ALOGV("%s: Tag %d does not have any entries", __FUNCTION__, tag);
- } else {
- ALOGV("%s: Tag %d had an entry, but it had 0 data", __FUNCTION__, tag);
- }
- return NULL;
+ if (!metadata->exists(tag)) {
+ ALOGV("%s: Tag %d does not have any entries", __FUNCTION__, tag);
+ return NULL;
+ } else {
+ // OK: we will return a 0-sized array.
+ ALOGV("%s: Tag %d had an entry, but it had 0 data", __FUNCTION__,
+ tag);
+ }
}
jsize byteCount = entry.count * tagSize;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5693d666adc2..5522aa09a32e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8479,6 +8479,18 @@
android:protectionLevel="internal"
android:featureFlag="android.content.pm.verification_service" />
+ <!--
+ This permission allows the system to receive PACKAGE_CHANGED broadcasts when the component
+ state of a non-exported component has been changed.
+ <p>Not for use by third-party applications. </p>
+ <p>Protection level: internal
+ @hide
+ -->
+ <permission
+ android:name="android.permission.RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED"
+ android:protectionLevel="internal"
+ android:featureFlag="android.content.pm.reduce_broadcasts_for_component_state_changes"/>
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
new file mode 100644
index 000000000000..8b2afa86986c
--- /dev/null
+++ b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
@@ -0,0 +1,23 @@
+<!--
+ ~ 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="?attr/disabledAlpha"
+ android:color="?attr/materialColorOnSurface" />
+ <item android:state_enabled="true"
+ android:color="?attr/materialColorPrimary" />
+</selector> \ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml
new file mode 100644
index 000000000000..cefc9121b7a4
--- /dev/null
+++ b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml
@@ -0,0 +1,23 @@
+<!--
+ ~ 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="?attr/primaryContentAlpha"
+ android:color="?attr/materialColorOnSurface" />
+ <item android:state_enabled="true"
+ android:color="?attr/materialColorOnPrimary" />
+</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
index 530d752732c1..eaf9e7d50bbd 100644
--- a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+ ~ 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.
@@ -14,12 +13,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.UdfpsKeyguardViewLegacy
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/udfps_animation_view_legacy"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <!-- Add fingerprint views here. See udfps_keyguard_view_internal.xml. -->
-
-</com.android.systemui.biometrics.UdfpsKeyguardViewLegacy>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="?attr/disabledAlpha"
+ android:color="?attr/materialColorOnSurface" />
+ <item android:state_enabled="true"
+ android:color="?attr/materialColorSurfaceContainer" />
+</selector> \ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
new file mode 100644
index 000000000000..94e50fbe2533
--- /dev/null
+++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
@@ -0,0 +1,23 @@
+<!--
+ ~ 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="?attr/primaryContentAlpha"
+ android:color="?attr/materialColorOnSurface" />
+ <item android:state_enabled="true"
+ android:color="?attr/materialColorOnSurface" />
+</selector> \ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml
new file mode 100644
index 000000000000..0029de14e34a
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ 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.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/btn_material_filled_background_color"/>
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ <size
+ android:width="@dimen/btn_material_width"
+ android:height="@dimen/btn_material_height" />
+ </shape>
+ </item>
+</ripple> \ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml
new file mode 100644
index 000000000000..105f077cd841
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ 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.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/btn_material_filled_tonal_background_color"/>
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ <size
+ android:width="@dimen/btn_material_width"
+ android:height="@dimen/btn_material_height" />
+ </shape>
+ </item>
+</ripple> \ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_piano.xml b/core/res/res/drawable/ic_zen_mode_icon_piano.xml
new file mode 100644
index 000000000000..012b9398d687
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_piano.xml
@@ -0,0 +1,25 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?android:attr/colorControlNormal"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L330,760L330,580L320,580Q303,580 291.5,568.5Q280,557 280,540L280,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760ZM630,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L680,200L680,540Q680,557 668.5,568.5Q657,580 640,580L630,580L630,760ZM390,760L570,760L570,580L560,580Q543,580 531.5,568.5Q520,557 520,540L520,200L440,200L440,540Q440,557 428.5,568.5Q417,580 400,580L390,580L390,760Z" />
+</vector> \ No newline at end of file
diff --git a/core/res/res/drawable/notification_progress.xml b/core/res/res/drawable/notification_progress.xml
new file mode 100644
index 000000000000..3a6b60077045
--- /dev/null
+++ b/core/res/res/drawable/notification_progress.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@id/background"
+ android:gravity="center_vertical|fill_horizontal">
+ <com.android.internal.widget.NotificationProgressDrawable
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:segSegGap="@dimen/notification_progress_segSeg_gap"
+ android:segPointGap="@dimen/notification_progress_segPoint_gap">
+ <segments
+ android:color="?attr/colorProgressBackgroundNormal"
+ android:dashGap="@dimen/notification_progress_segments_dash_gap"
+ android:dashWidth="@dimen/notification_progress_segments_dash_width"
+ android:width="@dimen/notification_progress_segments_height" />
+ <points
+ android:color="?attr/colorProgressBackgroundNormal"
+ android:radius="@dimen/notification_progress_points_radius"
+ android:cornerRadius="@dimen/notification_progress_points_corner_radius"
+ android:inset="@dimen/notification_progress_points_inset" />
+ </com.android.internal.widget.NotificationProgressDrawable>
+ </item>
+</layer-list>
diff --git a/core/res/res/layout/notification_template_material_progress.xml b/core/res/res/layout/notification_template_material_progress.xml
index fdcefccdbf16..75827a279ff7 100644
--- a/core/res/res/layout/notification_template_material_progress.xml
+++ b/core/res/res/layout/notification_template_material_progress.xml
@@ -75,12 +75,11 @@
/>
- <com.android.internal.widget.NotificationProgressBar
- android:id="@+id/progress"
+ <include
android:layout_width="0dp"
- android:layout_height="@dimen/notification_progress_bar_height"
- style="@style/Widget.Material.Light.ProgressBar.Horizontal"
android:layout_weight="1"
+ android:layout_height="@dimen/notification_progress_tracker_height"
+ layout="@layout/notification_template_notification_progress_bar"
/>
<com.android.internal.widget.CachingIconView
diff --git a/packages/SystemUI/res/layout/udfps_bp_view.xml b/core/res/res/layout/notification_template_notification_progress_bar.xml
index f1c55ef16cdc..35748962cfb2 100644
--- a/packages/SystemUI/res/layout/udfps_bp_view.xml
+++ b/core/res/res/layout/notification_template_notification_progress_bar.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+ ~ Copyright (C) 2014 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.
@@ -12,11 +12,12 @@
~ 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.
+ ~ limitations under the License
-->
-<com.android.systemui.biometrics.UdfpsBpView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/udfps_animation_view"
+
+<com.android.internal.widget.NotificationProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/progress"
android:layout_width="match_parent"
- android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsBpView>
+ android:layout_height="@dimen/notification_progress_tracker_height"
+ style="@style/Widget.Material.Notification.NotificationProgressBar"
+ />
diff --git a/core/res/res/values-watch-v36/colors.xml b/core/res/res/values-watch-v36/colors.xml
new file mode 100644
index 000000000000..4bc2a66fa206
--- /dev/null
+++ b/core/res/res/values-watch-v36/colors.xml
@@ -0,0 +1,18 @@
+<!--
+ ~ 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.
+ -->
+<!-- TODO(b/372524566): update color token's value to match material3 design. -->
+<resources>
+</resources> \ No newline at end of file
diff --git a/core/res/res/values-watch-v36/config.xml b/core/res/res/values-watch-v36/config.xml
new file mode 100644
index 000000000000..c8f347afb318
--- /dev/null
+++ b/core/res/res/values-watch-v36/config.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <!-- Overrides system value -->
+ <dimen name="config_buttonCornerRadius">26dp</dimen>
+</resources>
diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml
new file mode 100644
index 000000000000..ad3c1a3ef3a1
--- /dev/null
+++ b/core/res/res/values-watch-v36/dimens_material.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ 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.
+ -->
+<resources>
+ <!-- values for material3 button -->
+ <dimen name="btn_material_width">172dp</dimen>
+ <dimen name="btn_material_height">52dp</dimen>
+ <dimen name="btn_horizontal_edge_padding">14dp</dimen>
+ <dimen name="btn_drawable_padding">6dp</dimen>
+ <dimen name="btn_lineHeight">18sp</dimen>
+ <dimen name="btn_textSize">15sp</dimen>
+
+ <!-- Opacity factor for disabled material3 widget -->
+ <dimen name="disabled_alpha_device_default">0.12</dimen>
+ <dimen name="primary_content_alpha_device_default">0.38</dimen>
+</resources>
diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml
new file mode 100644
index 000000000000..32a22bb755cb
--- /dev/null
+++ b/core/res/res/values-watch-v36/styles_material.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <!-- Button Styles -->
+ <!-- Material Button - Filled -->
+ <style name="Widget.DeviceDefault.Button.Filled" parent="Widget.DeviceDefault.Button">
+ <item name="android:background">@drawable/btn_background_material_filled</item>
+ <item name="textAppearance">@style/TextAppearance.Widget.Button.Material.Filled</item>
+ </style>
+
+ <!-- Material Button - Filled Tonal(Override system default button styles) -->
+ <style name="Widget.DeviceDefault.Button">
+ <item name="background">@drawable/btn_background_material_filled_tonal</item>
+ <item name="textAppearance">@style/TextAppearance.Widget.Button.Material</item>
+ <item name="minHeight">@dimen/btn_material_height</item>
+ <item name="maxWidth">@dimen/btn_material_width</item>
+ <item name="android:paddingStart">@dimen/btn_horizontal_edge_padding</item>
+ <item name="android:paddingEnd">@dimen/btn_horizontal_edge_padding</item>
+ <item name="android:drawablePadding">@dimen/btn_drawable_padding</item>
+ <item name="android:maxLines">2</item>
+ <item name="android:ellipsize">end</item>
+ <item name="android:breakStrategy">simple</item>
+ <item name="stateListAnimator">@anim/button_state_list_anim_material</item>
+ <item name="focusable">true</item>
+ <item name="clickable">true</item>
+ <item name="gravity">center_vertical</item>
+ </style>
+
+ <!-- Text Styles -->
+ <!-- TextAppearance for Material Button - Filled -->
+ <style name="TextAppearance.Widget.Button.Material.Filled" parent="TextAppearance.Widget.Button.Material">
+ <item name="textColor">@color/btn_material_filled_text_color</item>
+ </style>
+
+ <!-- TextAppearance for Material Button - Filled Tonal -->
+ <style name="TextAppearance.Widget.Button.Material" parent="TextAppearance.DeviceDefault">
+ <item name="android:fontFamily">font-family-flex-device-default</item>
+ <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item>
+ <item name="textSize">@dimen/btn_textSize</item>
+ <item name="textColor">@color/btn_material_filled_tonal_text_color</item>
+ <item name="lineHeight">@dimen/btn_lineHeight</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8a2d767e9ad1..91b482051065 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -287,6 +287,11 @@
vibration params. -->
<integer name="config_requestVibrationParamsTimeout">50</integer>
+ <!-- The max duration (in milliseconds) that the vibrator service will allow effects to be
+ pipelined (i.e. service will wait for ongoing vibration to finish instead of cancelling it
+ to start the new one). Value should be positive. Zero will disable effect pipelining. -->
+ <integer name="config_vibrationPipelineMaxDuration">25</integer>
+
<!-- Array containing the usages that should request vibration params before they are played.
These usages don't have strong latency requirements, e.g. ringtone and notification, and
can be slightly delayed. -->
@@ -4447,17 +4452,25 @@
<string-array translatable="false" name="config_defaultPinnerServiceFiles">
</string-array>
- <!-- True if camera app should be pinned via Pinner Service -->
+ <!-- Note: This config is deprecated, use config_pinnerCameraPinBytes instead.
+ True if camera app should be pinned via Pinner Service -->
<bool name="config_pinnerCameraApp">false</bool>
- <!-- Bytes that the PinnerService will pin for Home app -->
- <integer name="config_pinnerHomePinBytes">0</integer>
+ <!-- Default: 60 MB. Bytes that the PinnerService will pin for Home app -->
+ <integer name="config_pinnerHomePinBytes">62914560</integer>
+
+ <!-- Default: 80 MB. Bytes that the PinnerService will pin for Camera app -->
+ <integer name="config_pinnerCameraPinBytes">83886080</integer>
- <!-- True if assistant app should be pinned via Pinner Service -->
+ <!-- Note: This config is deprecated, use config_pinnerAssistantPinBytes instead.
+ True if assistant app should be pinned via Pinner Service -->
<bool name="config_pinnerAssistantApp">false</bool>
- <!-- Bytes that the PinnerService will pin for WebView -->
- <integer name="config_pinnerWebviewPinBytes">0</integer>
+ <!-- Default: 60 MB. Bytes that the PinnerService will pin for Assistant -->
+ <integer name="config_pinnerAssistantPinBytes">62914560</integer>
+
+ <!-- Default: 20 MB. Bytes that the PinnerService will pin for WebView -->
+ <integer name="config_pinnerWebviewPinBytes">20971520</integer>
<!-- Maximum memory that PinnerService will pin for apps expressed
as a percentage of total device memory [0,100].
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b92aa2f355ed..7184d9a8c890 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -817,6 +817,22 @@
<dimen name="notification_progress_tracker_width">40dp</dimen>
<!-- The size of the progress tracker height -->
<dimen name="notification_progress_tracker_height">20dp</dimen>
+ <!-- The gap between segments in the notification progress bar -->
+ <dimen name="notification_progress_segSeg_gap">2dp</dimen>
+ <!-- The gap between a segment and a point in the notification progress bar -->
+ <dimen name="notification_progress_segPoint_gap">4dp</dimen>
+ <!-- The dash gap of the notification progress bar segments -->
+ <dimen name="notification_progress_segments_dash_gap">9dp</dimen>
+ <!-- The dash width of the notification progress bar segments -->
+ <dimen name="notification_progress_segments_dash_width">3dp</dimen>
+ <!-- The height of the notification progress bar segments -->
+ <dimen name="notification_progress_segments_height">6dp</dimen>
+ <!-- The radius of the notification progress bar points -->
+ <dimen name="notification_progress_points_radius">10dp</dimen>
+ <!-- The corner radius of the notification progress bar points drawn as rects -->
+ <dimen name="notification_progress_points_corner_radius">4dp</dimen>
+ <!-- The inset of the notification progress bar points drawn as rects -->
+ <dimen name="notification_progress_points_inset">0dp</dimen>
<!-- The maximum size of the small notification icon on low memory devices. -->
<dimen name="notification_small_icon_size_low_ram">@dimen/notification_small_icon_size</dimen>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index eec6ae3fb521..cb8e4aa9b2a9 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -502,6 +502,10 @@ please see styles_device_defaults.xml.
<style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
+ <style name="Widget.Material.Notification.NotificationProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal">
+ <item name="progressDrawable">@drawable/notification_progress</item>
+ </style>
+
<style name="Widget.Material.Notification.Text" parent="Widget.Material.Light.TextView">
<item name="lineHeight">20sp</item>
<item name="textAppearance">@style/TextAppearance.Material.Notification</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4f63fac96a8d..0348b4685a66 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2141,6 +2141,7 @@
<java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
<java-symbol type="bool" name="config_ignoreVibrationsOnWirelessCharger" />
<java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" />
+ <java-symbol type="integer" name="config_vibrationPipelineMaxDuration" />
<java-symbol type="integer" name="config_radioScanningTimeout" />
<java-symbol type="integer" name="config_requestVibrationParamsTimeout" />
<java-symbol type="array" name="config_requestVibrationParamsForUsages" />
@@ -3471,6 +3472,8 @@
<java-symbol type="array" name="config_defaultPinnerServiceFiles" />
<java-symbol type="bool" name="config_pinnerCameraApp" />
<java-symbol type="integer" name="config_pinnerHomePinBytes" />
+ <java-symbol type="integer" name="config_pinnerCameraPinBytes" />
+ <java-symbol type="integer" name="config_pinnerAssistantPinBytes" />
<java-symbol type="bool" name="config_pinnerAssistantApp" />
<java-symbol type="integer" name="config_pinnerWebviewPinBytes" />
<java-symbol type="integer" name="config_pinnerMaxPinnedMemoryPercentage" />
@@ -3864,6 +3867,14 @@
<java-symbol type="dimen" name="notification_progress_icon_size" />
<java-symbol type="dimen" name="notification_progress_tracker_width" />
<java-symbol type="dimen" name="notification_progress_tracker_height" />
+ <java-symbol type="dimen" name="notification_progress_segSeg_gap" />
+ <java-symbol type="dimen" name="notification_progress_segPoint_gap" />
+ <java-symbol type="dimen" name="notification_progress_segments_dash_gap" />
+ <java-symbol type="dimen" name="notification_progress_segments_dash_width" />
+ <java-symbol type="dimen" name="notification_progress_segments_height" />
+ <java-symbol type="dimen" name="notification_progress_points_radius" />
+ <java-symbol type="dimen" name="notification_progress_points_corner_radius" />
+ <java-symbol type="dimen" name="notification_progress_points_inset" />
<java-symbol type="dimen" name="notification_small_icon_size_low_ram"/>
<java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/>
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 48e26203fab4..23a09857032c 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -2397,10 +2397,25 @@ public class NotificationTest {
public void progressStyle_getProgressMax_returnsSumOfSegmentLength() {
final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
progressStyle
+ .setProgressSegments(List.of(new Notification.ProgressStyle.Segment(15),
+ new Notification.ProgressStyle.Segment(25)))
.addProgressSegment(new Notification.ProgressStyle.Segment(10))
.addProgressSegment(new Notification.ProgressStyle.Segment(20));
- assertThat(progressStyle.getProgressMax()).isEqualTo(30);
+ assertThat(progressStyle.getProgressMax()).isEqualTo(70);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+ public void progressStyle_getProgressMax_onSetProgressSegments_resets() {
+ final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
+ progressStyle
+ .addProgressSegment(new Notification.ProgressStyle.Segment(10))
+ .addProgressSegment(new Notification.ProgressStyle.Segment(20))
+ .setProgressSegments(List.of(new Notification.ProgressStyle.Segment(15),
+ new Notification.ProgressStyle.Segment(25)));
+
+ assertThat(progressStyle.getProgressMax()).isEqualTo(40);
}
@Test
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
index 5636f9bc436c..6d14ccb18c53 100644
--- a/core/tests/coretests/src/android/app/OWNERS
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -14,3 +14,5 @@ per-file KeyguardManagerTest.java = file:/services/core/java/com/android/server/
# Files related to background activity launches
per-file Background*Start* = file:/BAL_OWNERS
+# Files related to caching
+per-file PropertyInvalidatedCache* = file:/PERFORMANCE_OWNERS
diff --git a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
index 987f68d4f9e1..80255c5f6600 100644
--- a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
+++ b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
@@ -16,6 +16,9 @@
package android.content.pm.verify;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_WARN;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -23,6 +26,8 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.content.pm.SharedLibraryInfo;
@@ -73,6 +78,7 @@ public class VerificationSessionTest {
private static final long TEST_EXTEND_TIME = 2000L;
private static final String TEST_KEY = "test key";
private static final String TEST_VALUE = "test value";
+ private static final int TEST_POLICY = VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
private final ArrayList<SharedLibraryInfo> mTestDeclaredLibraries = new ArrayList<>();
private final PersistableBundle mTestExtensionParams = new PersistableBundle();
@@ -90,7 +96,7 @@ public class VerificationSessionTest {
mTestExtensionParams.putString(TEST_KEY, TEST_VALUE);
mTestSession = new VerificationSession(TEST_ID, TEST_INSTALL_SESSION_ID,
TEST_PACKAGE_NAME, TEST_PACKAGE_URI, TEST_SIGNING_INFO, mTestDeclaredLibraries,
- mTestExtensionParams, mTestSessionInterface, mTestCallback);
+ mTestExtensionParams, TEST_POLICY, mTestSessionInterface, mTestCallback);
}
@Test
@@ -118,6 +124,7 @@ public class VerificationSessionTest {
// structure is different, but all the key/value pairs should be preserved as before.
assertThat(sessionFromParcel.getExtensionParams().getString(TEST_KEY))
.isEqualTo(mTestExtensionParams.getString(TEST_KEY));
+ assertThat(sessionFromParcel.getVerificationPolicy()).isEqualTo(TEST_POLICY);
}
@Test
@@ -152,4 +159,42 @@ public class VerificationSessionTest {
verify(mTestCallback, times(1)).reportVerificationIncomplete(
eq(TEST_ID), eq(reason));
}
+
+ @Test
+ public void testPolicyNoOverride() {
+ assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY);
+ // This "set" is a no-op
+ assertThat(mTestSession.setVerificationPolicy(TEST_POLICY)).isTrue();
+ assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY);
+ verifyZeroInteractions(mTestSessionInterface);
+ }
+
+ @Test
+ public void testPolicyOverrideFail() throws Exception {
+ final int newPolicy = VERIFICATION_POLICY_BLOCK_FAIL_WARN;
+ when(mTestSessionInterface.setVerificationPolicy(anyInt(), anyInt())).thenReturn(false);
+ assertThat(mTestSession.setVerificationPolicy(newPolicy)).isFalse();
+ verify(mTestSessionInterface, times(1))
+ .setVerificationPolicy(eq(TEST_ID), eq(newPolicy));
+ // Next "get" should not trigger binder call because the previous "set" has failed
+ assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY);
+ verifyNoMoreInteractions(mTestSessionInterface);
+ }
+
+ @Test
+ public void testPolicyOverrideSuccess() throws Exception {
+ final int newPolicy = VERIFICATION_POLICY_BLOCK_FAIL_WARN;
+ when(mTestSessionInterface.setVerificationPolicy(anyInt(), anyInt())).thenReturn(true);
+ assertThat(mTestSession.setVerificationPolicy(newPolicy)).isTrue();
+ verify(mTestSessionInterface, times(1))
+ .setVerificationPolicy(eq(TEST_ID), eq(newPolicy));
+ assertThat(mTestSession.getVerificationPolicy()).isEqualTo(newPolicy);
+ assertThat(mTestSession.getVerificationPolicy()).isEqualTo(newPolicy);
+
+ // Setting back to the original policy should still trigger binder calls
+ assertThat(mTestSession.setVerificationPolicy(TEST_POLICY)).isTrue();
+ verify(mTestSessionInterface, times(1))
+ .setVerificationPolicy(eq(TEST_ID), eq(TEST_POLICY));
+ assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY);
+ }
}
diff --git a/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java b/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java
index 7f73a1eb4b48..7807c8a94530 100644
--- a/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java
+++ b/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java
@@ -16,6 +16,8 @@
package android.content.pm.verify;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
@@ -52,6 +54,7 @@ public class VerifierServiceTest {
private static final String TEST_PACKAGE_NAME = "com.foo";
private static final Uri TEST_PACKAGE_URI = Uri.parse("test://test");
private static final SigningInfo TEST_SIGNING_INFO = new SigningInfo();
+ private static final int TEST_POLICY = VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
private VerifierService mService;
private VerificationSession mSession;
@@ -60,8 +63,7 @@ public class VerifierServiceTest {
mService = Mockito.mock(VerifierService.class, Answers.CALLS_REAL_METHODS);
mSession = new VerificationSession(TEST_ID, TEST_INSTALL_SESSION_ID,
TEST_PACKAGE_NAME, TEST_PACKAGE_URI, TEST_SIGNING_INFO,
- new ArrayList<>(),
- new PersistableBundle(), null, null);
+ new ArrayList<>(), new PersistableBundle(), TEST_POLICY, null, null);
}
@Test
diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS
index 1c0073417c9a..6149382d0800 100644
--- a/core/tests/coretests/src/android/os/OWNERS
+++ b/core/tests/coretests/src/android/os/OWNERS
@@ -9,3 +9,6 @@ per-file PowerManager*.java = file:/services/core/java/com/android/server/power/
# PerformanceHintManager
per-file PerformanceHintManagerTest.java = file:/ADPF_OWNERS
+
+# Caching
+per-file IpcDataCache* = file:/PERFORMANCE_OWNERS
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 25f9cb7c1088..6311298ec8cf 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -42,6 +42,7 @@ import android.text.Layout.Alignment;
import android.text.style.ForegroundColorSpan;
import android.text.style.StrikethroughSpan;
+import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -697,7 +698,7 @@ public class LayoutTest {
if (drawCommand.path != null) {
expect.that(drawCommand.path).isEqualTo(selectionPath);
- expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+ expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.YELLOW);
expect.that(drawCommand.paint.getBlendMode()).isNotNull();
highlightsFound++;
} else if (drawCommand.text != null) {
@@ -750,7 +751,7 @@ public class LayoutTest {
if (drawCommand.path != null) {
expect.that(drawCommand.path).isEqualTo(selectionPath);
- expect.that(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+ expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.YELLOW);
expect.that(drawCommand.paint.getBlendMode()).isNotNull();
highlightsFound++;
} else if (drawCommand.text != null) {
@@ -802,7 +803,7 @@ public class LayoutTest {
if (drawCommand.path != null) {
expect.that(drawCommand.path).isEqualTo(selectionPath);
- expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+ expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.CYAN);
expect.that(drawCommand.paint.getBlendMode()).isNull();
highlightsFound++;
} else if (drawCommand.text != null) {
@@ -855,7 +856,7 @@ public class LayoutTest {
if (drawCommand.path != null) {
expect.that(drawCommand.path).isEqualTo(selectionPath);
- expect.that(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+ expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.CYAN);
expect.that(drawCommand.paint.getBlendMode()).isNull();
highlightsFound++;
} else if (drawCommand.text != null) {
@@ -914,7 +915,7 @@ public class LayoutTest {
if (drawCommand.rect != null) {
numBackgroundsFound++;
- expect.that(drawCommand.paint.getColor()).isEqualTo(Color.BLACK);
+ expect.that(removeAlpha(drawCommand.paint.getColor())).isEqualTo(Color.BLACK);
expect.that(drawCommand.rect.height()).isAtLeast(LINE_HEIGHT);
expect.that(drawCommand.rect.width()).isGreaterThan(0);
float expectedY = (numBackgroundsFound) * (LINE_HEIGHT + LINE_DESCENT);
@@ -997,20 +998,38 @@ public class LayoutTest {
.filter(it -> it.rect != null)
.toList();
- expect.that(backgroundCommands.get(0).paint.getColor()).isEqualTo(Color.BLACK);
- expect.that(backgroundCommands.get(1).paint.getColor()).isEqualTo(Color.WHITE);
- expect.that(backgroundCommands.get(2).paint.getColor()).isEqualTo(Color.WHITE);
- expect.that(backgroundCommands.get(3).paint.getColor()).isEqualTo(Color.WHITE);
- expect.that(backgroundCommands.get(4).paint.getColor()).isEqualTo(Color.WHITE);
- expect.that(backgroundCommands.get(5).paint.getColor()).isEqualTo(Color.BLACK);
- expect.that(backgroundCommands.get(6).paint.getColor()).isEqualTo(Color.BLACK);
- expect.that(backgroundCommands.get(7).paint.getColor()).isEqualTo(Color.BLACK);
- expect.that(backgroundCommands.get(8).paint.getColor()).isEqualTo(Color.BLACK);
- expect.that(backgroundCommands.get(9).paint.getColor()).isEqualTo(Color.BLACK);
+ expect.that(removeAlpha(backgroundCommands.get(0).paint.getColor()))
+ .isEqualTo(Color.BLACK);
+ expect.that(removeAlpha(backgroundCommands.get(1).paint.getColor()))
+ .isEqualTo(Color.WHITE);
+ expect.that(removeAlpha(backgroundCommands.get(2).paint.getColor()))
+ .isEqualTo(Color.WHITE);
+ expect.that(removeAlpha(backgroundCommands.get(3).paint.getColor()))
+ .isEqualTo(Color.WHITE);
+ expect.that(removeAlpha(backgroundCommands.get(4).paint.getColor()))
+ .isEqualTo(Color.WHITE);
+ expect.that(removeAlpha(backgroundCommands.get(5).paint.getColor()))
+ .isEqualTo(Color.BLACK);
+ expect.that(removeAlpha(backgroundCommands.get(6).paint.getColor()))
+ .isEqualTo(Color.BLACK);
+ expect.that(removeAlpha(backgroundCommands.get(7).paint.getColor()))
+ .isEqualTo(Color.BLACK);
+ expect.that(removeAlpha(backgroundCommands.get(8).paint.getColor()))
+ .isEqualTo(Color.BLACK);
+ expect.that(removeAlpha(backgroundCommands.get(9).paint.getColor()))
+ .isEqualTo(Color.BLACK);
expect.that(backgroundCommands.size()).isEqualTo(backgroundRectsDrawn);
}
+ private int removeAlpha(int color) {
+ return Color.rgb(
+ Color.red(color),
+ Color.green(color),
+ Color.blue(color)
+ );
+ }
+
private static final class MockCanvas extends Canvas {
static class DrawCommand {
@@ -1122,6 +1141,11 @@ public class LayoutTest {
mDrawCommands.add(new DrawCommand(rect, p));
}
+ @Override
+ public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
+ mDrawCommands.add(new DrawCommand(rect, paint));
+ }
+
List<DrawCommand> getDrawCommands() {
return mDrawCommands;
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 632721126714..ed9fc1c9e547 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -71,6 +71,7 @@ import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Binder;
import android.os.SystemProperties;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -82,6 +83,7 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
+import android.view.accessibility.AccessibilityManager;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -1628,6 +1630,42 @@ public class ViewRootImplTest {
});
}
+ @Test
+ @EnableFlags(android.view.accessibility.Flags.FLAG_FOCUS_RECT_MIN_SIZE)
+ public void testAdjustAccessibilityFocusedBounds_largeEnoughBoundsAreUnchanged() {
+ final int strokeWidth = sContext.getSystemService(AccessibilityManager.class)
+ .getAccessibilityFocusStrokeWidth();
+ final int left, top, width, height;
+ left = top = 100;
+ width = height = strokeWidth * 2;
+ final Rect bounds = new Rect(left, top, left + width, top + height);
+ final Rect originalBounds = new Rect(bounds);
+
+ mViewRootImpl.adjustAccessibilityFocusedRectBoundsIfNeeded(bounds);
+
+ assertThat(bounds).isEqualTo(originalBounds);
+ }
+
+ @Test
+ @EnableFlags(android.view.accessibility.Flags.FLAG_FOCUS_RECT_MIN_SIZE)
+ public void testAdjustAccessibilityFocusedBounds_smallBoundsAreExpanded() {
+ final int strokeWidth = sContext.getSystemService(AccessibilityManager.class)
+ .getAccessibilityFocusStrokeWidth();
+ final int left, top, width, height;
+ left = top = 100;
+ width = height = strokeWidth;
+ final Rect bounds = new Rect(left, top, left + width, top + height);
+ final Rect originalBounds = new Rect(bounds);
+
+ mViewRootImpl.adjustAccessibilityFocusedRectBoundsIfNeeded(bounds);
+
+ // Bounds should be centered on the same point, but expanded to at least strokeWidth * 2
+ assertThat(bounds.centerX()).isEqualTo(originalBounds.centerX());
+ assertThat(bounds.centerY()).isEqualTo(originalBounds.centerY());
+ assertThat(bounds.width()).isAtLeast(strokeWidth * 2);
+ assertThat(bounds.height()).isAtLeast(strokeWidth * 2);
+ }
+
private boolean setForceDarkSysProp(boolean isForceDarkEnabled) {
try {
SystemProperties.set(
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
index 6419c1e07f2e..79a478a7676b 100644
--- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
@@ -254,7 +254,6 @@ public class NotificationProgressBarTest {
// Colors with 50% opacity
int fadedGreen = 0x7F00FF00;
- int fadedBlue = 0x7F0000FF;
int fadedYellow = 0x7FFFFF00;
List<Part> expected = new ArrayList<>(List.of(
diff --git a/core/tests/vibrator/src/android/os/CombinedVibrationTest.java b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java
index 244fcff7d27d..37ddfd21c98d 100644
--- a/core/tests/vibrator/src/android/os/CombinedVibrationTest.java
+++ b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java
@@ -22,6 +22,9 @@ import static junit.framework.Assert.assertTrue;
import static org.testng.Assert.assertThrows;
+import android.hardware.vibrator.IVibrator;
+import android.util.SparseArray;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -134,6 +137,54 @@ public class CombinedVibrationTest {
}
@Test
+ public void testDurationMono_withVibratorSupportingPrimitives() {
+ SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+ infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5)
+ .build());
+ infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+ .build());
+
+ // Use max duration from all vibrators.
+ assertEquals(10, CombinedVibration.createParallel(
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).getDuration(infos));
+ assertEquals(111, CombinedVibration.createParallel(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose())
+ .getDuration(infos));
+ }
+
+ @Test
+ public void testDurationMono_withVibratorNotSupportingPrimitives() {
+ SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+ infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build());
+ infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+ .build());
+
+ // Use max duration from all vibrators.
+ assertEquals(-1, CombinedVibration.createParallel(
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK)).getDuration(infos));
+ assertEquals(-1, CombinedVibration.createParallel(
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose())
+ .getDuration(infos));
+ }
+
+ @Test
public void testDurationStereo() {
assertEquals(6, CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.createOneShot(1, 1))
@@ -156,6 +207,75 @@ public class CombinedVibrationTest {
}
@Test
+ public void testDurationStereo_withVibratorSupportingPrimitives() {
+ SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+ infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5)
+ .build());
+ infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+ .build());
+
+ // Use specific vibrator durations, then max effect duration
+ assertEquals(111, CombinedVibration.startParallel()
+ .addVibrator(1, VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose())
+ .addVibrator(2, VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose())
+ .combine()
+ .getDuration(infos));
+ assertEquals(110, CombinedVibration.startParallel()
+ .addVibrator(1, VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose())
+ .combine()
+ .getDuration(infos));
+ }
+
+ @Test
+ public void testDurationStereo_withVibratorNotSupportingPrimitives() {
+ SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+ infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build());
+ infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+ .build());
+
+ // One vibrator does not support primitives
+ assertEquals(-1, CombinedVibration.startParallel()
+ .addVibrator(1, VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose())
+ .addVibrator(2, VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose())
+ .combine()
+ .getDuration(infos));
+ // Invalid vibrator ID
+ assertEquals(-1, CombinedVibration.startParallel()
+ .addVibrator(3, VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose())
+ .combine()
+ .getDuration(infos));
+ }
+
+ @Test
public void testDurationSequential() {
assertEquals(26, CombinedVibration.startSequential()
.addNext(1, VibrationEffect.createOneShot(10, 10), 10)
@@ -178,6 +298,59 @@ public class CombinedVibrationTest {
}
@Test
+ public void testDurationSequential_withVibratorSupportingPrimitives() {
+ SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+ infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5)
+ .build());
+ infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+ .build());
+
+ // Add each duration and delay
+ assertEquals(321, CombinedVibration.startSequential()
+ .addNext(1, VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose(), 100)
+ .addNext(2, VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose())
+ .combine()
+ .getDuration(infos));
+ }
+
+ @Test
+ public void testDurationSequential_withVibratorNotSupportingPrimitives() {
+ SparseArray<VibratorInfo> infos = new SparseArray<>(2);
+ infos.put(1, new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build());
+ infos.put(2, new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1)
+ .build());
+
+ assertEquals(-1, CombinedVibration.startSequential()
+ .addNext(1, VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose(), 100)
+ .addNext(2, VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose())
+ .combine()
+ .getDuration(infos));
+ }
+
+ @Test
public void testIsHapticFeedbackCandidateMono() {
assertTrue(CombinedVibration.createParallel(
VibrationEffect.createOneShot(1, 1)).isHapticFeedbackCandidate());
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index f5b04ee759a5..8acf2ed87e95 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -1078,6 +1078,52 @@ public class VibrationEffectTest {
}
@Test
+ public void testDuration_withVibratorSupportingPrimitives() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 5)
+ .build();
+
+ VibrationEffect composition = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 100)
+ .compose();
+
+ assertEquals(1, VibrationEffect.createOneShot(1, 1).getDuration());
+ assertEquals(10, VibrationEffect.get(VibrationEffect.EFFECT_CLICK).getDuration(info));
+ assertEquals(115, composition.getDuration(info));
+ assertEquals(Long.MAX_VALUE,
+ VibrationEffect.startComposition()
+ .repeatEffectIndefinitely(composition)
+ .compose()
+ .getDuration(info));
+ if (Flags.vendorVibrationEffects()) {
+ assertEquals(-1,
+ VibrationEffect.createVendorEffect(createNonEmptyBundle()).getDuration(info));
+ }
+ }
+
+ @Test
+ public void testDuration_withVibratorNotSupportingPrimitives() {
+ VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
+ .build();
+
+ VibrationEffect composition = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose();
+
+ assertEquals(-1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK).getDuration(info));
+ assertEquals(-1, composition.getDuration(info));
+ assertEquals(Long.MAX_VALUE,
+ VibrationEffect.startComposition()
+ .repeatEffectIndefinitely(composition)
+ .compose()
+ .getDuration(info));
+ }
+
+ @Test
public void testAreVibrationFeaturesSupported_allSegmentsSupported() {
VibratorInfo info = new VibratorInfo.Builder(/* id= */ 1)
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
index 7dd9e55f8f3e..f9ec5f0b2305 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
@@ -24,6 +24,7 @@ import static junit.framework.Assert.assertTrue;
import static org.testng.Assert.assertNotEquals;
import static org.testng.Assert.assertThrows;
+import android.hardware.vibrator.IVibrator;
import android.os.Parcel;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
@@ -114,39 +115,82 @@ public class PrebakedSegmentTest {
@Test
public void testDuration() {
- assertEquals(-1, new PrebakedSegment(
- VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
- .getDuration());
- assertEquals(-1, new PrebakedSegment(
- VibrationEffect.EFFECT_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
- .getDuration());
- assertEquals(-1, new PrebakedSegment(
- VibrationEffect.EFFECT_DOUBLE_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
- .getDuration());
- assertEquals(-1, new PrebakedSegment(
- VibrationEffect.EFFECT_THUD, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK).getDuration());
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TICK).getDuration());
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_THUD).getDuration());
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
.getDuration());
}
@Test
+ public void testDuration_withVibratorSupportingPrimitives_returnsPrimitiveDuration() {
+ int tickDuration = 5;
+ int clickDuration = 10;
+ int thudDuration = 15;
+
+ VibratorInfo vibratorInfo = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, tickDuration)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, clickDuration)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, thudDuration)
+ .build();
+
+ assertEquals(5, createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
+ .getDuration(vibratorInfo));
+ assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
+ .getDuration(vibratorInfo));
+ assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
+ .getDuration(vibratorInfo));
+ assertEquals(10, createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
+ .getDuration(vibratorInfo));
+ assertEquals(20, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
+ .getDuration(vibratorInfo));
+ assertEquals(15, createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
+ .getDuration(vibratorInfo));
+
+ // Unknown effects
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_POP)
+ .getDuration(vibratorInfo));
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.RINGTONES[0])
+ .getDuration(vibratorInfo));
+ }
+
+ @Test
+ public void testDuration_withVibratorNotSupportingPrimitives_returnsUnknown() {
+ VibratorInfo vibratorInfo = createVibratorInfoWithSupportedEffects(
+ VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_POP);
+
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
+ .getDuration(vibratorInfo));
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
+ .getDuration(vibratorInfo));
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
+ .getDuration(vibratorInfo));
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
+ .getDuration(vibratorInfo));
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
+ .getDuration(vibratorInfo));
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
+ .getDuration(vibratorInfo));
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.EFFECT_POP)
+ .getDuration(vibratorInfo));
+ assertEquals(-1, createSegmentWithFallback(VibrationEffect.RINGTONES[0])
+ .getDuration(vibratorInfo));
+ }
+
+ @Test
public void testIsHapticFeedbackCandidate_prebakedConstants_areCandidates() {
- assertTrue(new PrebakedSegment(
- VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK)
.isHapticFeedbackCandidate());
- assertTrue(new PrebakedSegment(
- VibrationEffect.EFFECT_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK)
.isHapticFeedbackCandidate());
- assertTrue(new PrebakedSegment(
- VibrationEffect.EFFECT_DOUBLE_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK)
.isHapticFeedbackCandidate());
- assertTrue(new PrebakedSegment(
- VibrationEffect.EFFECT_HEAVY_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK)
.isHapticFeedbackCandidate());
- assertTrue(new PrebakedSegment(
- VibrationEffect.EFFECT_THUD, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_THUD)
.isHapticFeedbackCandidate());
- assertTrue(new PrebakedSegment(
- VibrationEffect.EFFECT_TEXTURE_TICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK)
.isHapticFeedbackCandidate());
}
@@ -271,8 +315,7 @@ public class PrebakedSegmentTest {
@Test
public void testIsHapticFeedbackCandidate_prebakedRingtones_notCandidates() {
- assertFalse(new PrebakedSegment(
- VibrationEffect.RINGTONES[1], true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
+ assertFalse(createSegmentWithFallback(VibrationEffect.RINGTONES[1])
.isHapticFeedbackCandidate());
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
index 97f1d5e77ddb..a6d9dc51d7bb 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
@@ -201,6 +201,22 @@ public class PrimitiveSegmentTest {
}
@Test
+ public void testDuration_withVibratorSupportingPrimitives_returnsVibratorDurationWithDelay() {
+ VibratorInfo vibratorInfo = createVibratorInfoWithSupportedPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK, /* durationMs= */ 10);
+ assertEquals(15, new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 5).getDuration(vibratorInfo));
+ }
+
+ @Test
+ public void testDuration_withVibratorNotSupportingPrimitive_returnsUnknown() {
+ VibratorInfo vibratorInfo = createVibratorInfoWithSupportedPrimitive(
+ VibrationEffect.Composition.PRIMITIVE_CLICK);
+ assertEquals(-1, new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_NOOP, 1, 5).getDuration(vibratorInfo));
+ }
+
+ @Test
public void testVibrationFeaturesSupport_primitiveSupportedByVibrator() {
assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK)
.areVibrationFeaturesSupported(
@@ -252,9 +268,14 @@ public class PrimitiveSegmentTest {
}
private static VibratorInfo createVibratorInfoWithSupportedPrimitive(int primitiveId) {
+ return createVibratorInfoWithSupportedPrimitive(primitiveId, /* durationMs= */ 10);
+ }
+
+ private static VibratorInfo createVibratorInfoWithSupportedPrimitive(int primitiveId,
+ int durationMs) {
return new VibratorInfo.Builder(/* id= */ 1)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitive(primitiveId, 10)
+ .setSupportedPrimitive(primitiveId, durationMs)
.build();
}
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
index bea82931dda7..df874bcb73ca 100644
--- a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
@@ -195,7 +195,14 @@ public class RampSegmentTest {
@Test
public void testDuration() {
+ VibratorInfo infoWithSupport =
+ createVibInfo(/* hasAmplitudeControl= */ true, /* hasFrequencyControl= */ true);
+ VibratorInfo infoWithoutSupport =
+ createVibInfo(/* hasAmplitudeControl= */ false, /* hasFrequencyControl= */ false);
+
assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration());
+ assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration(infoWithSupport));
+ assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration(infoWithoutSupport));
}
@Test
diff --git a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
index 411074a75e2e..914117c10c87 100644
--- a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
@@ -213,7 +213,13 @@ public class StepSegmentTest {
@Test
public void testDuration() {
+ VibratorInfo infoWithSupport = createVibInfoForAmplitude(/* hasAmplitudeControl= */ true);
+ VibratorInfo infoWithoutSupport =
+ createVibInfoForAmplitude(/* hasAmplitudeControl= */ false);
+
assertEquals(5, new StepSegment(0, 0, 5).getDuration());
+ assertEquals(5, new StepSegment(0, 0, 5).getDuration(infoWithSupport));
+ assertEquals(5, new StepSegment(0, 0, 5).getDuration(infoWithoutSupport));
}
@Test
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 2e72f0ec90b8..4aa7e96f352d 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -572,6 +572,7 @@ applications that come with the platform
<permission name="android.permission.READ_BLOCKED_NUMBERS" />
<!-- Permission required for CTS test - PackageManagerTest -->
<permission name="android.permission.DOMAIN_VERIFICATION_AGENT"/>
+ <permission name="android.permission.VERIFICATION_AGENT"/>
<!-- Permission required for CTS test CtsInputTestCases -->
<permission name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW" />
<!-- Permission required for CTS test - PackageManagerShellCommandInstallTest -->
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index c1d2235e3324..b559a15c3a60 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -22,6 +22,7 @@ java_library_host {
static_libs: [
"annotations",
+ "jsr305",
"framework-annotations-lib",
"//external/error_prone:error_prone_core",
],
diff --git a/errorprone/OWNERS b/errorprone/OWNERS
index bddbdb364683..aa8c126a32e1 100644
--- a/errorprone/OWNERS
+++ b/errorprone/OWNERS
@@ -1,2 +1 @@
-jsharkey@android.com
-jsharkey@google.com
+colefaust@google.com
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
index 8dc9579e6b52..6d5e44844a83 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
@@ -29,6 +29,7 @@ import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.util.ASTHelpers;
+import com.google.errorprone.util.ErrorProneComment;
import com.google.errorprone.util.ErrorProneToken;
import com.google.errorprone.util.ErrorProneTokens;
import com.sun.source.tree.ClassTree;
@@ -37,7 +38,6 @@ import com.sun.source.tree.MethodTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
-import com.sun.tools.javac.parser.Tokens;
import java.util.HashMap;
import java.util.Map;
@@ -66,7 +66,7 @@ public class HideInCommentsChecker extends BugChecker implements
final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree, state);
final String sourceCode = state.getSourceCode().toString();
for (ErrorProneToken token : ErrorProneTokens.getTokens(sourceCode, state.context)) {
- for (Tokens.Comment comment : token.comments()) {
+ for (ErrorProneComment comment : token.comments()) {
if (!javadocableTrees.containsKey(token.pos())) {
continue;
}
@@ -81,7 +81,7 @@ public class HideInCommentsChecker extends BugChecker implements
return NO_MATCH;
}
- private static Optional<SuggestedFix> generateFix(Tokens.Comment comment) {
+ private static Optional<SuggestedFix> generateFix(ErrorProneComment comment) {
final String text = comment.getText();
if (text.startsWith("/**")) {
return Optional.empty();
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index 671eb6e514c5..ed17fdefcb53 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -26,6 +26,7 @@ import android.graphics.Typeface;
import android.graphics.fonts.Font;
import com.android.internal.util.Preconditions;
+import com.android.text.flags.Flags;
import dalvik.annotation.optimization.CriticalNative;
@@ -132,6 +133,9 @@ public final class PositionedGlyphs {
@NonNull
public Font getFont(@IntRange(from = 0) int index) {
Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+ if (Flags.typefaceRedesign()) {
+ return mFonts.get(nGetFontId(mLayoutPtr, index));
+ }
return mFonts.get(index);
}
@@ -245,20 +249,29 @@ public final class PositionedGlyphs {
*/
public PositionedGlyphs(long layoutPtr, float xOffset, float yOffset) {
mLayoutPtr = layoutPtr;
- int glyphCount = nGetGlyphCount(layoutPtr);
- mFonts = new ArrayList<>(glyphCount);
mXOffset = xOffset;
mYOffset = yOffset;
- long prevPtr = 0;
- Font prevFont = null;
- for (int i = 0; i < glyphCount; ++i) {
- long ptr = nGetFont(layoutPtr, i);
- if (prevPtr != ptr) {
- prevPtr = ptr;
- prevFont = new Font(ptr);
+ if (Flags.typefaceRedesign()) {
+ int fontCount = nGetFontCount(layoutPtr);
+ mFonts = new ArrayList<>(fontCount);
+ for (int i = 0; i < fontCount; ++i) {
+ mFonts.add(new Font(nGetFontRef(layoutPtr, i)));
+ }
+ } else {
+ int glyphCount = nGetGlyphCount(layoutPtr);
+ mFonts = new ArrayList<>(glyphCount);
+
+ long prevPtr = 0;
+ Font prevFont = null;
+ for (int i = 0; i < glyphCount; ++i) {
+ long ptr = nGetFont(layoutPtr, i);
+ if (prevPtr != ptr) {
+ prevPtr = ptr;
+ prevFont = new Font(ptr);
+ }
+ mFonts.add(prevFont);
}
- mFonts.add(prevFont);
}
NoImagePreloadHolder.REGISTRY.registerNativeAllocation(this, layoutPtr);
@@ -290,6 +303,12 @@ public final class PositionedGlyphs {
private static native float nGetWeightOverride(long minikinLayout, int i);
@CriticalNative
private static native float nGetItalicOverride(long minikinLayout, int i);
+ @CriticalNative
+ private static native int nGetFontCount(long minikinLayout);
+ @CriticalNative
+ private static native long nGetFontRef(long minikinLayout, int fontId);
+ @CriticalNative
+ private static native int nGetFontId(long minikinLayout, int glyphIndex);
@Override
public boolean equals(Object o) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index 882a8d035e93..ad194f707cf3 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -1255,7 +1255,8 @@ class DividerPresenter implements View.OnTouchListener {
// Update divider line surface visibility and color.
// If a container is fully expanded, the divider line is invisible unless dragging.
- final boolean isDividerLineVisible = !mProperties.mIsDraggableExpandType || mIsDragging;
+ final boolean isDividerLineVisible = mProperties.mDividerWidthPx > 0
+ && (!mProperties.mIsDraggableExpandType || mIsDragging);
t.setVisibility(mDividerLineSurface, isDividerLineVisible);
t.setColor(mDividerLineSurface, colorToFloatArray(
Color.valueOf(mProperties.mDividerAttributes.getDividerColor())));
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 96ffa03a1f65..52ce8cb5c0dc 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -67,6 +67,7 @@ class BubbleStackViewTest {
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var positioner: BubblePositioner
+ private lateinit var bubbleLogger: BubbleLogger
private lateinit var iconFactory: BubbleIconFactory
private lateinit var expandedViewManager: FakeBubbleExpandedViewManager
private lateinit var bubbleStackView: BubbleStackView
@@ -96,10 +97,11 @@ class BubbleStackViewTest {
)
)
positioner = BubblePositioner(context, windowManager)
+ bubbleLogger = BubbleLogger(UiEventLoggerFake())
bubbleData =
BubbleData(
context,
- BubbleLogger(UiEventLoggerFake()),
+ bubbleLogger,
positioner,
BubbleEducationController(context),
shellExecutor,
@@ -394,6 +396,7 @@ class BubbleStackViewTest {
expandedViewManager,
bubbleTaskViewFactory,
positioner,
+ bubbleLogger,
bubbleStackView,
null,
iconFactory,
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
index 9fdde128ce41..712cc7c475bc 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
@@ -29,6 +29,7 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
+import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.ProtoLog
import com.android.internal.statusbar.IStatusBarService
import com.android.launcher3.icons.BubbleIconFactory
@@ -70,6 +71,7 @@ class BubbleViewInfoTaskTest {
private lateinit var bgExecutor: TestExecutor
private lateinit var bubbleStackView: BubbleStackView
private lateinit var bubblePositioner: BubblePositioner
+ private lateinit var bubbleLogger: BubbleLogger
private lateinit var expandedViewManager: BubbleExpandedViewManager
private val bubbleTaskViewFactory = BubbleTaskViewFactory {
@@ -103,10 +105,11 @@ class BubbleViewInfoTaskTest {
mainExecutor
)
bubblePositioner = BubblePositioner(context, windowManager)
+ bubbleLogger = BubbleLogger(UiEventLoggerFake())
val bubbleData =
BubbleData(
context,
- mock<BubbleLogger>(),
+ bubbleLogger,
bubblePositioner,
BubbleEducationController(context),
mainExecutor,
@@ -138,7 +141,7 @@ class BubbleViewInfoTaskTest {
WindowManagerShellWrapper(mainExecutor),
mock<UserManager>(),
mock<LauncherApps>(),
- mock<BubbleLogger>(),
+ bubbleLogger,
mock<TaskStackListenerImpl>(),
mock<ShellTaskOrganizer>(),
bubblePositioner,
@@ -314,6 +317,7 @@ class BubbleViewInfoTaskTest {
expandedViewManager,
bubbleTaskViewFactory,
bubblePositioner,
+ bubbleLogger,
bubbleStackView,
null /* layerView */,
iconFactory,
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 35d459f27534..fa9d2baa78d9 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
@@ -18,6 +18,7 @@ package com.android.wm.shell.bubbles.bar
import android.app.ActivityManager
import android.content.Context
+import android.content.pm.ShortcutInfo
import android.graphics.Insets
import android.graphics.Rect
import android.view.LayoutInflater
@@ -27,11 +28,13 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.BubbleData
import com.android.wm.shell.bubbles.BubbleExpandedViewManager
+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
@@ -43,11 +46,14 @@ import com.android.wm.shell.shared.handles.RegionSamplingHelper
import com.android.wm.shell.taskview.TaskView
import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import java.util.Collections
import java.util.concurrent.Executor
@@ -70,14 +76,18 @@ class BubbleBarExpandedViewTest {
private lateinit var expandedViewManager: BubbleExpandedViewManager
private lateinit var positioner: BubblePositioner
private lateinit var bubbleTaskView: BubbleTaskView
+ private lateinit var bubble: Bubble
private lateinit var bubbleExpandedView: BubbleBarExpandedView
private var testableRegionSamplingHelper: TestableRegionSamplingHelper? = null
private var regionSamplingProvider: TestRegionSamplingProvider? = null
+ private val bubbleLogger = spy(BubbleLogger(UiEventLoggerFake()))
+
@Before
fun setUp() {
ProtoLog.REQUIRE_PROTOLOGTOOL = false
+ ProtoLog.init()
mainExecutor = TestExecutor()
bgExecutor = TestExecutor()
positioner = BubblePositioner(context, windowManager)
@@ -106,11 +116,12 @@ class BubbleBarExpandedViewTest {
bubbleExpandedView.initialize(
expandedViewManager,
positioner,
+ bubbleLogger,
false /* isOverflow */,
bubbleTaskView,
mainExecutor,
bgExecutor,
- regionSamplingProvider
+ regionSamplingProvider,
)
getInstrumentation().runOnMainSync(Runnable {
@@ -118,6 +129,20 @@ class BubbleBarExpandedViewTest {
// Helper should be created once attached to window
testableRegionSamplingHelper = regionSamplingProvider!!.helper
})
+
+ bubble = Bubble(
+ "key",
+ ShortcutInfo.Builder(context, "id").build(),
+ 100 /* desiredHeight */,
+ 0 /* desiredHeightResId */,
+ "title",
+ 0 /* taskId */,
+ null /* locus */,
+ true /* isDismissable */,
+ directExecutor(),
+ directExecutor()
+ ) {}
+ bubbleExpandedView.update(bubble)
}
@After
@@ -191,6 +216,16 @@ class BubbleBarExpandedViewTest {
assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
}
+ @Test
+ fun testEventLogging_dismissBubbleViaAppMenu() {
+ getInstrumentation().runOnMainSync { bubbleExpandedView.handleView.performClick() }
+ val dismissMenuItem =
+ bubbleExpandedView.findViewWithTag<View>(BubbleBarMenuView.DISMISS_ACTION_TAG)
+ assertThat(dismissMenuItem).isNotNull()
+ getInstrumentation().runOnMainSync { dismissMenuItem.performClick() }
+ verify(bubbleLogger).log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_APP_MENU)
+ }
+
private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
override fun create(): BubbleTaskView {
val taskViewTaskController = mock<TaskViewTaskController>()
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
index 35ef2393bb9b..37596182f05b 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -38,7 +38,7 @@
<Button
android:layout_width="94dp"
android:layout_height="60dp"
- android:id="@+id/maximize_menu_maximize_button"
+ android:id="@+id/maximize_menu_size_toggle_button"
style="?android:attr/buttonBarButtonStyle"
android:stateListAnimator="@null"
android:importantForAccessibility="yes"
@@ -48,7 +48,7 @@
android:alpha="0"/>
<TextView
- android:id="@+id/maximize_menu_maximize_window_text"
+ android:id="@+id/maximize_menu_size_toggle_button_text"
android:layout_width="94dp"
android:layout_height="18dp"
android:textSize="11sp"
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 1f1565160965..df1e2248872b 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -492,8 +492,12 @@
<dimen name="desktop_mode_maximize_menu_buttons_outline_stroke">1dp</dimen>
<!-- The radius of the inner fill of the maximize menu buttons. -->
<dimen name="desktop_mode_maximize_menu_buttons_fill_radius">4dp</dimen>
- <!-- The padding between the outline and fill of the maximize menu buttons. -->
- <dimen name="desktop_mode_maximize_menu_buttons_fill_padding">4dp</dimen>
+ <!-- The padding between the outline and fill of the maximize menu snap and maximize buttons. -->
+ <dimen name="desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding">4dp</dimen>
+ <!-- The vertical padding between the outline and fill of the maximize menu restore button. -->
+ <dimen name="desktop_mode_maximize_menu_restore_button_fill_vertical_padding">13dp</dimen>
+ <!-- The horizontal padding between the outline and fill of the maximize menu restore button. -->
+ <dimen name="desktop_mode_maximize_menu_restore_button_fill_horizontal_padding">21dp</dimen>
<!-- The corner radius of the maximize menu. -->
<dimen name="desktop_mode_maximize_menu_corner_radius">8dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 621e2aacd673..afac9f6433a3 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -319,6 +319,8 @@
<string name="desktop_mode_non_resizable_snap_text">App can\'t be moved here</string>
<!-- Accessibility text for the Maximize Menu's maximize button [CHAR LIMIT=NONE] -->
<string name="desktop_mode_maximize_menu_maximize_button_text">Maximize</string>
+ <!-- Accessibility text for the Maximize Menu's restore button [CHAR LIMIT=NONE] -->
+ <string name="desktop_mode_maximize_menu_restore_button_text">Restore</string>
<!-- Accessibility text for the Maximize Menu's snap left button [CHAR LIMIT=NONE] -->
<string name="desktop_mode_maximize_menu_snap_left_button_text">Snap left</string>
<!-- Accessibility text for the Maximize Menu's snap right button [CHAR LIMIT=NONE] -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index e3fc5c2273e2..e8e25e20d8d8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -572,6 +572,7 @@ public class Bubble implements BubbleViewProvider {
* @param expandedViewManager the bubble expanded view manager.
* @param taskViewFactory the task view factory used to create the task view for the bubble.
* @param positioner the bubble positioner.
+ * @param bubbleLogger log bubble metrics.
* @param stackView the view the bubble is added to, iff showing as floating.
* @param layerView the layer the bubble is added to, iff showing in the bubble bar.
* @param iconFactory the icon factory used to create images for the bubble.
@@ -581,6 +582,7 @@ public class Bubble implements BubbleViewProvider {
BubbleExpandedViewManager expandedViewManager,
BubbleTaskViewFactory taskViewFactory,
BubblePositioner positioner,
+ BubbleLogger bubbleLogger,
@Nullable BubbleStackView stackView,
@Nullable BubbleBarLayerView layerView,
BubbleIconFactory iconFactory,
@@ -595,6 +597,7 @@ public class Bubble implements BubbleViewProvider {
expandedViewManager,
taskViewFactory,
positioner,
+ bubbleLogger,
stackView,
layerView,
iconFactory,
@@ -616,6 +619,7 @@ public class Bubble implements BubbleViewProvider {
expandedViewManager,
taskViewFactory,
positioner,
+ bubbleLogger,
stackView,
layerView,
iconFactory,
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 c81ffdabf9b8..37e8ead4fc78 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
@@ -892,7 +892,7 @@ public class BubbleController implements ConfigurationChangeListener,
registerBroadcastReceiver();
if (isShowingAsBubbleBar()) {
mBubbleData.getOverflow().initializeForBubbleBar(
- mExpandedViewManager, mBubblePositioner);
+ mExpandedViewManager, mBubblePositioner, mLogger);
} else {
mBubbleData.getOverflow().initialize(
mExpandedViewManager, mStackView, mBubblePositioner);
@@ -1100,6 +1100,7 @@ public class BubbleController implements ConfigurationChangeListener,
mExpandedViewManager,
mBubbleTaskViewFactory,
mBubblePositioner,
+ mLogger,
mStackView,
mLayerView,
mBubbleIconFactory,
@@ -1111,6 +1112,7 @@ public class BubbleController implements ConfigurationChangeListener,
mExpandedViewManager,
mBubbleTaskViewFactory,
mBubblePositioner,
+ mLogger,
mStackView,
mLayerView,
mBubbleIconFactory,
@@ -1246,9 +1248,12 @@ public class BubbleController implements ConfigurationChangeListener,
*/
public void dragBubbleToDismiss(String bubbleKey, long timestamp) {
String selectedBubbleKey = mBubbleData.getSelectedBubbleKey();
- if (mBubbleData.hasAnyBubbleWithKey(bubbleKey)) {
+ Bubble bubbleToDismiss = mBubbleData.getAnyBubbleWithkey(bubbleKey);
+ if (bubbleToDismiss != null) {
mBubbleData.dismissBubbleWithKey(
bubbleKey, Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER, timestamp);
+ mLogger.log(bubbleToDismiss,
+ BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE);
}
if (mBubbleData.hasBubbles()) {
// We still have bubbles, if we dragged an individual bubble to dismiss we were expanded
@@ -1582,6 +1587,7 @@ public class BubbleController implements ConfigurationChangeListener,
mExpandedViewManager,
mBubbleTaskViewFactory,
mBubblePositioner,
+ mLogger,
mStackView,
mLayerView,
mBubbleIconFactory,
@@ -1644,6 +1650,7 @@ public class BubbleController implements ConfigurationChangeListener,
mExpandedViewManager,
mBubbleTaskViewFactory,
mBubblePositioner,
+ mLogger,
mStackView,
mLayerView,
mBubbleIconFactory,
@@ -1724,6 +1731,7 @@ public class BubbleController implements ConfigurationChangeListener,
mExpandedViewManager,
mBubbleTaskViewFactory,
mBubblePositioner,
+ mLogger,
mStackView,
mLayerView,
mBubbleIconFactory,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 709a7bdc61f2..4de9dfa54c5d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -495,7 +495,7 @@ public class BubbleData {
/**
* When this method is called it is expected that all info in the bubble has completed loading.
* @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleExpandedViewManager,
- * BubbleTaskViewFactory, BubblePositioner, BubbleStackView,
+ * BubbleTaskViewFactory, BubblePositioner, BubbleLogger, BubbleStackView,
* com.android.wm.shell.bubbles.bar.BubbleBarLayerView,
* com.android.launcher3.icons.BubbleIconFactory, boolean)
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 68c4657f2b68..c74412b825d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -73,17 +73,19 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl
fun initializeForBubbleBar(
expandedViewManager: BubbleExpandedViewManager,
- positioner: BubblePositioner
+ positioner: BubblePositioner,
+ bubbleLogger: BubbleLogger,
) {
createBubbleBarExpandedView()
.initialize(
expandedViewManager,
positioner,
+ bubbleLogger,
/* isOverflow= */ true,
/* bubbleTaskView= */ null,
/* mainExecutor= */ null,
/* backgroundExecutor= */ null,
- /* regionSamplingProvider= */ null
+ /* regionSamplingProvider= */ null,
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 39fb2f49c1ee..96b6043059d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -73,6 +73,7 @@ public class BubbleViewInfoTask {
private final WeakReference<BubbleExpandedViewManager> mExpandedViewManager;
private final WeakReference<BubbleTaskViewFactory> mTaskViewFactory;
private final WeakReference<BubblePositioner> mPositioner;
+ private final WeakReference<BubbleLogger> mBubbleLogger;
private final WeakReference<BubbleStackView> mStackView;
private final WeakReference<BubbleBarLayerView> mLayerView;
private final BubbleIconFactory mIconFactory;
@@ -94,6 +95,7 @@ public class BubbleViewInfoTask {
BubbleExpandedViewManager expandedViewManager,
BubbleTaskViewFactory taskViewFactory,
BubblePositioner positioner,
+ BubbleLogger bubbleLogger,
@Nullable BubbleStackView stackView,
@Nullable BubbleBarLayerView layerView,
BubbleIconFactory factory,
@@ -106,6 +108,7 @@ public class BubbleViewInfoTask {
mExpandedViewManager = new WeakReference<>(expandedViewManager);
mTaskViewFactory = new WeakReference<>(taskViewFactory);
mPositioner = new WeakReference<>(positioner);
+ mBubbleLogger = new WeakReference<>(bubbleLogger);
mStackView = new WeakReference<>(stackView);
mLayerView = new WeakReference<>(layerView);
mIconFactory = factory;
@@ -221,8 +224,9 @@ public class BubbleViewInfoTask {
ProtoLog.v(WM_SHELL_BUBBLES, "Task initializing bubble bar expanded view key=%s",
mBubble.getKey());
viewInfo.bubbleBarExpandedView.initialize(mExpandedViewManager.get(),
- mPositioner.get(), false /* isOverflow */, viewInfo.taskView,
- mMainExecutor, mBgExecutor, new RegionSamplingProvider() {
+ mPositioner.get(), mBubbleLogger.get(), false /* isOverflow */,
+ viewInfo.taskView, mMainExecutor, mBgExecutor,
+ new RegionSamplingProvider() {
@Override
public RegionSamplingHelper createHelper(View sampledView,
RegionSamplingHelper.SamplingCallback callback,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
index e9a593392dc2..c1da94cc470f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
@@ -78,6 +78,7 @@ public class BubbleViewInfoTaskLegacy extends
private WeakReference<BubbleExpandedViewManager> mExpandedViewManager;
private WeakReference<BubbleTaskViewFactory> mTaskViewFactory;
private WeakReference<BubblePositioner> mPositioner;
+ private WeakReference<BubbleLogger> mBubbleLogger;
private WeakReference<BubbleStackView> mStackView;
private WeakReference<BubbleBarLayerView> mLayerView;
private BubbleIconFactory mIconFactory;
@@ -95,6 +96,7 @@ public class BubbleViewInfoTaskLegacy extends
BubbleExpandedViewManager expandedViewManager,
BubbleTaskViewFactory taskViewFactory,
BubblePositioner positioner,
+ BubbleLogger bubbleLogger,
@Nullable BubbleStackView stackView,
@Nullable BubbleBarLayerView layerView,
BubbleIconFactory factory,
@@ -107,6 +109,7 @@ public class BubbleViewInfoTaskLegacy extends
mExpandedViewManager = new WeakReference<>(expandedViewManager);
mTaskViewFactory = new WeakReference<>(taskViewFactory);
mPositioner = new WeakReference<>(positioner);
+ mBubbleLogger = new WeakReference<>(bubbleLogger);
mStackView = new WeakReference<>(stackView);
mLayerView = new WeakReference<>(layerView);
mIconFactory = factory;
@@ -124,8 +127,9 @@ public class BubbleViewInfoTaskLegacy extends
}
if (mLayerView.get() != null) {
return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(),
- mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory,
- mBubble, mSkipInflation, mMainExecutor, mBackgroundExecutor);
+ mTaskViewFactory.get(), mPositioner.get(), mBubbleLogger.get(),
+ mLayerView.get(), mIconFactory, mBubble, mSkipInflation, mMainExecutor,
+ mBackgroundExecutor);
} else {
return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(),
mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory,
@@ -187,6 +191,7 @@ public class BubbleViewInfoTaskLegacy extends
BubbleExpandedViewManager expandedViewManager,
BubbleTaskViewFactory taskViewFactory,
BubblePositioner positioner,
+ BubbleLogger bubbleLogger,
BubbleBarLayerView layerView,
BubbleIconFactory iconFactory,
Bubble b,
@@ -200,9 +205,9 @@ public class BubbleViewInfoTaskLegacy extends
LayoutInflater inflater = LayoutInflater.from(c);
info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate(
R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */);
- info.bubbleBarExpandedView.initialize(
- expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView,
- mainExecutor, backgroundExecutor, new RegionSamplingProvider() {
+ info.bubbleBarExpandedView.initialize(expandedViewManager, positioner, bubbleLogger,
+ false /* isOverflow */, bubbleTaskView, mainExecutor, backgroundExecutor,
+ new RegionSamplingProvider() {
@Override
public RegionSamplingHelper createHelper(View sampledView,
RegionSamplingHelper.SamplingCallback callback,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 2a9001728cc2..0ce651c3f1fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -39,6 +39,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleExpandedViewManager;
+import com.android.wm.shell.bubbles.BubbleLogger;
import com.android.wm.shell.bubbles.BubbleOverflowContainerView;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleTaskView;
@@ -90,6 +91,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
private Bubble mBubble;
private BubbleExpandedViewManager mManager;
private BubblePositioner mPositioner;
+ private BubbleLogger mBubbleLogger;
private boolean mIsOverflow;
private BubbleTaskViewHelper mBubbleTaskViewHelper;
private BubbleBarMenuViewController mMenuViewController;
@@ -178,6 +180,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
/** Initializes the view, must be called before doing anything else. */
public void initialize(BubbleExpandedViewManager expandedViewManager,
BubblePositioner positioner,
+ BubbleLogger bubbleLogger,
boolean isOverflow,
@Nullable BubbleTaskView bubbleTaskView,
@Nullable Executor mainExecutor,
@@ -185,6 +188,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
@Nullable RegionSamplingProvider regionSamplingProvider) {
mManager = expandedViewManager;
mPositioner = positioner;
+ mBubbleLogger = bubbleLogger;
mIsOverflow = isOverflow;
mMainExecutor = mainExecutor;
mBackgroundExecutor = backgroundExecutor;
@@ -248,6 +252,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
@Override
public void onDismissBubble(Bubble bubble) {
mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_GESTURE);
+ mBubbleLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_APP_MENU);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
index 0300869cbbe1..52b807abddd6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.bubbles.bar;
import android.annotation.ColorInt;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
@@ -41,6 +42,9 @@ import java.util.ArrayList;
* Bubble bar expanded view menu
*/
public class BubbleBarMenuView extends LinearLayout {
+
+ public static final Object DISMISS_ACTION_TAG = new Object();
+
private ViewGroup mBubbleSectionView;
private ViewGroup mActionsSectionView;
private ImageView mBubbleIconView;
@@ -119,6 +123,9 @@ public class BubbleBarMenuView extends LinearLayout {
R.layout.bubble_bar_menu_item, mActionsSectionView, false);
itemView.update(action.mIcon, action.mTitle, action.mTint);
itemView.setOnClickListener(action.mOnClick);
+ if (action.mTag != null) {
+ itemView.setTag(action.mTag);
+ }
mActionsSectionView.addView(itemView);
}
}
@@ -159,6 +166,8 @@ public class BubbleBarMenuView extends LinearLayout {
private Icon mIcon;
private @ColorInt int mTint;
private String mTitle;
+ @Nullable
+ private Object mTag;
private OnClickListener mOnClick;
MenuAction(Icon icon, String title, OnClickListener onClick) {
@@ -171,5 +180,14 @@ public class BubbleBarMenuView extends LinearLayout {
this.mTint = tint;
this.mOnClick = onClick;
}
+
+ MenuAction(Icon icon, String title, @ColorInt int tint, @Nullable Object tag,
+ OnClickListener onClick) {
+ this.mIcon = icon;
+ this.mTitle = title;
+ this.mTint = tint;
+ this.mTag = tag;
+ this.mOnClick = onClick;
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
index 514810745e10..5ed01b66ec67 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java
@@ -212,6 +212,7 @@ class BubbleBarMenuViewController {
Icon.createWithResource(resources, R.drawable.ic_remove_no_shadow),
resources.getString(R.string.bubble_dismiss_text),
tintColor,
+ BubbleBarMenuView.DISMISS_ACTION_TAG,
view -> {
hideMenu(true /* animated */);
if (mListener != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
index 0b2b3e7c41c8..85921703d559 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
@@ -87,7 +87,7 @@ public class DividerSnapAlgorithm {
private final boolean mCalculateRatiosBasedOnAvailableSpace;
/** Allows split ratios that go offscreen (a.k.a. "flexible split") */
private final boolean mAllowOffscreenRatios;
- private final boolean mIsHorizontalDivision;
+ private final boolean mIsLeftRightSplit;
/** The first target which is still splitting the screen */
private final SnapTarget mFirstSplitTarget;
@@ -101,13 +101,13 @@ public class DividerSnapAlgorithm {
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
- boolean isHorizontalDivision, Rect insets, int dockSide) {
- this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets,
- dockSide, false /* minimized */, true /* resizable */);
+ boolean isLeftRightSplit, Rect insets, int dockSide) {
+ this(res, displayWidth, displayHeight, dividerSize, isLeftRightSplit, insets,
+ dockSide, false /* minimized */, true /* resizable */);
}
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
- boolean isHorizontalDivision, Rect insets, int dockSide, boolean isMinimizedMode,
+ boolean isLeftRightSplit, Rect insets, int dockSide, boolean isMinimizedMode,
boolean isHomeResizable) {
mMinFlingVelocityPxPerSecond =
MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density;
@@ -116,7 +116,7 @@ public class DividerSnapAlgorithm {
mDividerSize = dividerSize;
mDisplayWidth = displayWidth;
mDisplayHeight = displayHeight;
- mIsHorizontalDivision = isHorizontalDivision;
+ mIsLeftRightSplit = isLeftRightSplit;
mInsets.set(insets);
mSnapMode = isMinimizedMode ? SNAP_MODE_MINIMIZED :
res.getInteger(com.android.internal.R.integer.config_dockedStackDividerSnapMode);
@@ -133,7 +133,7 @@ public class DividerSnapAlgorithm {
Flags.enableFlexibleTwoAppSplit() && SplitScreenUtils.allowOffscreenRatios(res);
mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize(
com.android.internal.R.dimen.task_height_of_minimized_mode) : 0;
- calculateTargets(isHorizontalDivision, dockSide);
+ calculateTargets(isLeftRightSplit, dockSide);
mFirstSplitTarget = mTargets.get(1);
mLastSplitTarget = mTargets.get(mTargets.size() - 2);
mDismissStartTarget = mTargets.get(0);
@@ -218,18 +218,18 @@ public class DividerSnapAlgorithm {
}
private int getStartInset() {
- if (mIsHorizontalDivision) {
- return mInsets.top;
- } else {
+ if (mIsLeftRightSplit) {
return mInsets.left;
+ } else {
+ return mInsets.top;
}
}
private int getEndInset() {
- if (mIsHorizontalDivision) {
- return mInsets.bottom;
- } else {
+ if (mIsLeftRightSplit) {
return mInsets.right;
+ } else {
+ return mInsets.bottom;
}
}
@@ -269,11 +269,11 @@ public class DividerSnapAlgorithm {
return mTargets.get(minIndex);
}
- private void calculateTargets(boolean isHorizontalDivision, int dockedSide) {
+ private void calculateTargets(boolean isLeftRightSplit, int dockedSide) {
mTargets.clear();
- int dividerMax = isHorizontalDivision
- ? mDisplayHeight
- : mDisplayWidth;
+ int dividerMax = isLeftRightSplit
+ ? mDisplayWidth
+ : mDisplayHeight;
int startPos = -mDividerSize;
if (dockedSide == DOCKED_RIGHT) {
startPos += mInsets.left;
@@ -281,38 +281,38 @@ public class DividerSnapAlgorithm {
mTargets.add(new SnapTarget(startPos, SNAP_TO_START_AND_DISMISS, 0.35f));
switch (mSnapMode) {
case SNAP_MODE_16_9:
- addRatio16_9Targets(isHorizontalDivision, dividerMax);
+ addRatio16_9Targets(isLeftRightSplit, dividerMax);
break;
case SNAP_FIXED_RATIO:
- addFixedDivisionTargets(isHorizontalDivision, dividerMax);
+ addFixedDivisionTargets(isLeftRightSplit, dividerMax);
break;
case SNAP_ONLY_1_1:
- addMiddleTarget(isHorizontalDivision);
+ addMiddleTarget(isLeftRightSplit);
break;
case SNAP_MODE_MINIMIZED:
- addMinimizedTarget(isHorizontalDivision, dockedSide);
+ addMinimizedTarget(isLeftRightSplit, dockedSide);
break;
}
mTargets.add(new SnapTarget(dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f));
}
- private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition,
+ private void addNonDismissingTargets(boolean isLeftRightSplit, int topPosition,
int bottomPosition, int dividerMax) {
@PersistentSnapPosition int firstTarget =
mAllowOffscreenRatios ? SNAP_TO_2_10_90 : SNAP_TO_2_33_66;
@PersistentSnapPosition int lastTarget =
mAllowOffscreenRatios ? SNAP_TO_2_90_10 : SNAP_TO_2_66_33;
maybeAddTarget(topPosition, topPosition - getStartInset(), firstTarget);
- addMiddleTarget(isHorizontalDivision);
+ addMiddleTarget(isLeftRightSplit);
maybeAddTarget(bottomPosition,
dividerMax - getEndInset() - (bottomPosition + mDividerSize), lastTarget);
}
- private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) {
- int start = isHorizontalDivision ? mInsets.top : mInsets.left;
- int end = isHorizontalDivision
- ? mDisplayHeight - mInsets.bottom
- : mDisplayWidth - mInsets.right;
+ private void addFixedDivisionTargets(boolean isLeftRightSplit, int dividerMax) {
+ int start = isLeftRightSplit ? mInsets.left : mInsets.top;
+ int end = isLeftRightSplit
+ ? mDisplayWidth - mInsets.right
+ : mDisplayHeight - mInsets.bottom;
int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2;
if (mAllowOffscreenRatios) {
@@ -323,23 +323,23 @@ public class DividerSnapAlgorithm {
}
int topPosition = start + size;
int bottomPosition = end - size - mDividerSize;
- addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax);
+ addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax);
}
- private void addRatio16_9Targets(boolean isHorizontalDivision, int dividerMax) {
- int start = isHorizontalDivision ? mInsets.top : mInsets.left;
- int end = isHorizontalDivision
- ? mDisplayHeight - mInsets.bottom
- : mDisplayWidth - mInsets.right;
- int startOther = isHorizontalDivision ? mInsets.left : mInsets.top;
- int endOther = isHorizontalDivision
+ private void addRatio16_9Targets(boolean isLeftRightSplit, int dividerMax) {
+ int start = isLeftRightSplit ? mInsets.left : mInsets.top;
+ int end = isLeftRightSplit
? mDisplayWidth - mInsets.right
: mDisplayHeight - mInsets.bottom;
+ int startOther = isLeftRightSplit ? mInsets.top : mInsets.left;
+ int endOther = isLeftRightSplit
+ ? mDisplayHeight - mInsets.bottom
+ : mDisplayWidth - mInsets.right;
float size = 9.0f / 16.0f * (endOther - startOther);
int sizeInt = (int) Math.floor(size);
int topPosition = start + sizeInt;
int bottomPosition = end - sizeInt - mDividerSize;
- addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax);
+ addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax);
}
/**
@@ -352,17 +352,17 @@ public class DividerSnapAlgorithm {
}
}
- private void addMiddleTarget(boolean isHorizontalDivision) {
- int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
+ private void addMiddleTarget(boolean isLeftRightSplit) {
+ int position = DockedDividerUtils.calculateMiddlePosition(isLeftRightSplit,
mInsets, mDisplayWidth, mDisplayHeight, mDividerSize);
mTargets.add(new SnapTarget(position, SNAP_TO_2_50_50));
}
- private void addMinimizedTarget(boolean isHorizontalDivision, int dockedSide) {
+ private void addMinimizedTarget(boolean isLeftRightSplit, int dockedSide) {
// In portrait offset the position by the statusbar height, in landscape add the statusbar
// height as well to match portrait offset
int position = mTaskHeightInMinimizedMode + mInsets.top;
- if (!isHorizontalDivision) {
+ if (isLeftRightSplit) {
if (dockedSide == DOCKED_LEFT) {
position += mInsets.left;
} else if (dockedSide == DOCKED_RIGHT) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java
index f25dfeafb32c..25157c05d0de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java
@@ -97,12 +97,12 @@ public class DockedDividerUtils {
}
}
- public static int calculateMiddlePosition(boolean isHorizontalDivision, Rect insets,
+ public static int calculateMiddlePosition(boolean isLeftRightSplit, Rect insets,
int displayWidth, int displayHeight, int dividerSize) {
- int start = isHorizontalDivision ? insets.top : insets.left;
- int end = isHorizontalDivision
- ? displayHeight - insets.bottom
- : displayWidth - insets.right;
+ int start = isLeftRightSplit ? insets.left : insets.top;
+ int end = isLeftRightSplit
+ ? displayWidth - insets.right
+ : displayHeight - insets.bottom;
return start + (end - start) / 2 - dividerSize / 2;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index c9c3aa0dd537..f73065ea8eb8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -665,7 +665,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
rootBounds.width(),
rootBounds.height(),
mDividerSize,
- !mIsLeftRightSplit,
+ mIsLeftRightSplit,
insets,
mIsLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 52262e68c401..97397cea4a38 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.dagger;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
@@ -727,7 +728,8 @@ public abstract class WMShellModule {
Transitions transitions,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
InteractionJankMonitor interactionJankMonitor) {
- return Flags.enableDesktopWindowingTransitions()
+ return (Flags.enableDesktopWindowingTransitions() ||
+ ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS.isTrue())
? new SpringDragToDesktopTransitionHandler(context, transitions,
rootTaskDisplayAreaOrganizer, interactionJankMonitor)
: new DefaultDragToDesktopTransitionHandler(context, transitions,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 379e052e7b38..8ebe503a3816 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -21,17 +21,39 @@ import com.android.internal.protolog.ProtoLog
import com.android.internal.util.FrameworkStatsLog
import com.android.window.flags.Flags
import com.android.wm.shell.EventLogTags
-import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import java.security.SecureRandom
+import java.util.Random
+import java.util.concurrent.atomic.AtomicInteger
+
/** Event logger for logging desktop mode session events */
class DesktopModeEventLogger {
+ private val random: Random = SecureRandom()
+
+ /** The session id for the current desktop mode session */
+ @VisibleForTesting
+ val currentSessionId: AtomicInteger = AtomicInteger(NO_SESSION_ID)
+
+ private fun generateSessionId() = 1 + random.nextInt(1 shl 20)
+
/**
- * Logs the enter of desktop mode having session id [sessionId] and the reason [enterReason] for
- * entering desktop mode
+ * Logs enter into desktop mode with [enterReason]
*/
- fun logSessionEnter(sessionId: Int, enterReason: EnterReason) {
+ fun logSessionEnter(enterReason: EnterReason) {
+ val sessionId = generateSessionId()
+ val previousSessionId = currentSessionId.getAndSet(sessionId)
+ if (previousSessionId != NO_SESSION_ID) {
+ ProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopModeLogger: Existing desktop mode session id: %s found on desktop "
+ + "mode enter",
+ previousSessionId
+ )
+ }
+
ProtoLog.v(
- ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging session enter, session: %s reason: %s",
sessionId,
enterReason.name
@@ -47,12 +69,20 @@ class DesktopModeEventLogger {
}
/**
- * Logs the exit of desktop mode having session id [sessionId] and the reason [exitReason] for
- * exiting desktop mode
+ * Logs exit from desktop mode session with [exitReason]
*/
- fun logSessionExit(sessionId: Int, exitReason: ExitReason) {
+ fun logSessionExit(exitReason: ExitReason) {
+ val sessionId = currentSessionId.getAndSet(NO_SESSION_ID)
+ if (sessionId == NO_SESSION_ID) {
+ ProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopModeLogger: No session id found for logging exit from desktop mode"
+ )
+ return
+ }
+
ProtoLog.v(
- ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging session exit, session: %s reason: %s",
sessionId,
exitReason.name
@@ -68,12 +98,20 @@ class DesktopModeEventLogger {
}
/**
- * Logs that the task with update [taskUpdate] was added in the desktop mode session having
- * session id [sessionId]
+ * Logs that a task with [taskUpdate] was added in a desktop mode session
*/
- fun logTaskAdded(sessionId: Int, taskUpdate: TaskUpdate) {
+ fun logTaskAdded(taskUpdate: TaskUpdate) {
+ val sessionId = currentSessionId.get()
+ if (sessionId == NO_SESSION_ID) {
+ ProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopModeLogger: No session id found for logging task added"
+ )
+ return
+ }
+
ProtoLog.v(
- ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging task added, session: %s taskId: %s",
sessionId,
taskUpdate.instanceId
@@ -85,12 +123,20 @@ class DesktopModeEventLogger {
}
/**
- * Logs that the task with update [taskUpdate] was removed in the desktop mode session having
- * session id [sessionId]
+ * Logs that a task with [taskUpdate] was removed from a desktop mode session
*/
- fun logTaskRemoved(sessionId: Int, taskUpdate: TaskUpdate) {
+ fun logTaskRemoved(taskUpdate: TaskUpdate) {
+ val sessionId = currentSessionId.get()
+ if (sessionId == NO_SESSION_ID) {
+ ProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopModeLogger: No session id found for logging task removed"
+ )
+ return
+ }
+
ProtoLog.v(
- ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging task remove, session: %s taskId: %s",
sessionId,
taskUpdate.instanceId
@@ -102,12 +148,20 @@ class DesktopModeEventLogger {
}
/**
- * Logs that the task with update [taskUpdate] had it's info changed in the desktop mode session
- * having session id [sessionId]
+ * Logs that a task with [taskUpdate] had it's info changed in a desktop mode session
*/
- fun logTaskInfoChanged(sessionId: Int, taskUpdate: TaskUpdate) {
+ fun logTaskInfoChanged(taskUpdate: TaskUpdate) {
+ val sessionId = currentSessionId.get()
+ if (sessionId == NO_SESSION_ID) {
+ ProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopModeLogger: No session id found for logging task info changed"
+ )
+ return
+ }
+
ProtoLog.v(
- ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging task info changed, session: %s taskId: %s",
sessionId,
taskUpdate.instanceId
@@ -119,14 +173,23 @@ class DesktopModeEventLogger {
}
/**
- * Logs that a task resize event is starting with [taskSizeUpdate] within a
- * Desktop mode [sessionId].
+ * Logs that a task resize event is starting with [taskSizeUpdate] within a Desktop mode
+ * session.
*/
- fun logTaskResizingStarted(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) {
+ fun logTaskResizingStarted(taskSizeUpdate: TaskSizeUpdate) {
if (!Flags.enableResizingMetrics()) return
+ val sessionId = currentSessionId.get()
+ if (sessionId == NO_SESSION_ID) {
+ ProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopModeLogger: No session id found for logging start of task resizing"
+ )
+ return
+ }
+
ProtoLog.v(
- ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging task resize is starting, session: %s taskId: %s",
sessionId,
taskSizeUpdate.instanceId
@@ -138,14 +201,22 @@ class DesktopModeEventLogger {
}
/**
- * Logs that a task resize event is ending with [taskSizeUpdate] within a
- * Desktop mode [sessionId].
+ * Logs that a task resize event is ending with [taskSizeUpdate] within a Desktop mode session.
*/
- fun logTaskResizingEnded(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) {
+ fun logTaskResizingEnded(taskSizeUpdate: TaskSizeUpdate) {
if (!Flags.enableResizingMetrics()) return
+ val sessionId = currentSessionId.get()
+ if (sessionId == NO_SESSION_ID) {
+ ProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopModeLogger: No session id found for logging end of task resizing"
+ )
+ return
+ }
+
ProtoLog.v(
- ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
+ WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging task resize is ending, session: %s taskId: %s",
sessionId,
taskSizeUpdate.instanceId
@@ -248,6 +319,7 @@ class DesktopModeEventLogger {
}
companion object {
+
/**
* Describes a task position and dimensions.
*
@@ -465,5 +537,6 @@ class DesktopModeEventLogger {
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE
private const val DESKTOP_MODE_TASK_SIZE_UPDATED_ATOM_ID =
FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED
+ @VisibleForTesting const val NO_SESSION_ID = 0
}
-}
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index f847aa8918c2..ed03982d191d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -35,8 +35,6 @@ import androidx.core.util.isEmpty
import androidx.core.util.isNotEmpty
import androidx.core.util.plus
import androidx.core.util.putAll
-import com.android.internal.logging.InstanceId
-import com.android.internal.logging.InstanceIdSequence
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
@@ -48,8 +46,8 @@ import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
@@ -65,8 +63,6 @@ class DesktopModeLoggerTransitionObserver(
private val desktopModeEventLogger: DesktopModeEventLogger
) : Transitions.TransitionObserver {
- private val idSequence: InstanceIdSequence by lazy { InstanceIdSequence(Int.MAX_VALUE) }
-
init {
if (DesktopModeStatus.canEnterDesktopMode(context)) {
shellInit.addInitCallback(this::onInit, this)
@@ -87,15 +83,7 @@ class DesktopModeLoggerTransitionObserver(
// following enter reason could be Screen On
private var wasPreviousTransitionExitByScreenOff: Boolean = false
- // The instanceId for the current logging session
- private var loggerInstanceId: InstanceId? = null
-
- private val isSessionActive: Boolean
- get() = loggerInstanceId != null
-
- private fun setSessionInactive() {
- loggerInstanceId = null
- }
+ @VisibleForTesting var isSessionActive: Boolean = false
fun onInit() {
transitions.registerObserver(this)
@@ -247,38 +235,32 @@ class DesktopModeLoggerTransitionObserver(
) {
// Sessions is finishing, log task updates followed by an exit event
identifyAndLogTaskUpdates(
- loggerInstanceId!!.id,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks
)
desktopModeEventLogger.logSessionExit(
- loggerInstanceId!!.id,
getExitReason(transitionInfo)
)
-
- setSessionInactive()
+ isSessionActive = false
} else if (
postTransitionVisibleFreeformTasks.isNotEmpty() &&
preTransitionVisibleFreeformTasks.isEmpty() &&
!isSessionActive
) {
// Session is starting, log enter event followed by task updates
- loggerInstanceId = idSequence.newInstanceId()
+ isSessionActive = true
desktopModeEventLogger.logSessionEnter(
- loggerInstanceId!!.id,
getEnterReason(transitionInfo)
)
identifyAndLogTaskUpdates(
- loggerInstanceId!!.id,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks
)
} else if (isSessionActive) {
// Session is neither starting, nor finishing, log task updates if there are any
identifyAndLogTaskUpdates(
- loggerInstanceId!!.id,
preTransitionVisibleFreeformTasks,
postTransitionVisibleFreeformTasks
)
@@ -291,7 +273,6 @@ class DesktopModeLoggerTransitionObserver(
/** Compare the old and new state of taskInfos and identify and log the changes */
private fun identifyAndLogTaskUpdates(
- sessionId: Int,
preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>
) {
@@ -302,7 +283,7 @@ class DesktopModeLoggerTransitionObserver(
when {
// new tasks added
previousTaskInfo == null -> {
- desktopModeEventLogger.logTaskAdded(sessionId, currentTaskUpdate)
+ desktopModeEventLogger.logTaskAdded(currentTaskUpdate)
Trace.setCounter(
Trace.TRACE_TAG_WINDOW_MANAGER,
VISIBLE_TASKS_COUNTER_NAME,
@@ -315,14 +296,14 @@ class DesktopModeLoggerTransitionObserver(
// TODO(b/347935387): Log changes only once they are stable.
buildTaskUpdateForTask(previousTaskInfo, postTransitionVisibleFreeformTasks.size())
!= currentTaskUpdate ->
- desktopModeEventLogger.logTaskInfoChanged(sessionId, currentTaskUpdate)
+ desktopModeEventLogger.logTaskInfoChanged(currentTaskUpdate)
}
}
// find old tasks that were removed
preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo ->
if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) {
- desktopModeEventLogger.logTaskRemoved(sessionId,
+ desktopModeEventLogger.logTaskRemoved(
buildTaskUpdateForTask(taskInfo, postTransitionVisibleFreeformTasks.size()))
Trace.setCounter(
Trace.TRACE_TAG_WINDOW_MANAGER,
@@ -417,13 +398,6 @@ class DesktopModeLoggerTransitionObserver(
visibleFreeformTaskInfos.set(taskInfo.taskId, taskInfo)
}
- @VisibleForTesting fun getLoggerSessionId(): Int? = loggerInstanceId?.id
-
- @VisibleForTesting
- fun setLoggerSessionId(id: Int) {
- loggerInstanceId = InstanceId.fakeInstanceId(id)
- }
-
private fun TransitionInfo.Change.requireTaskInfo(): RunningTaskInfo {
return this.taskInfo ?: throw IllegalStateException("Expected TaskInfo in the Change")
}
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 52b92a89abdf..61de0777ed05 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
@@ -378,11 +378,12 @@ public class DesktopModeVisualIndicator {
private static VisualIndicatorAnimator fadeBoundsIn(
@NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) {
- final Rect startBounds = getIndicatorBounds(displayLayout, type);
+ final Rect endBounds = getIndicatorBounds(displayLayout, type);
+ final Rect startBounds = getMinBounds(endBounds);
view.getBackground().setBounds(startBounds);
final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
- view, startBounds, getMaxBounds(startBounds));
+ view, startBounds, endBounds);
animator.setInterpolator(new DecelerateInterpolator());
setupIndicatorAnimation(animator, AlphaAnimType.ALPHA_FADE_IN_ANIM);
return animator;
@@ -390,8 +391,8 @@ public class DesktopModeVisualIndicator {
private static VisualIndicatorAnimator fadeBoundsOut(
@NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) {
- final Rect endBounds = getIndicatorBounds(displayLayout, type);
- final Rect startBounds = getMaxBounds(endBounds);
+ final Rect startBounds = getIndicatorBounds(displayLayout, type);
+ final Rect endBounds = getMinBounds(startBounds);
view.getBackground().setBounds(startBounds);
final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
@@ -422,28 +423,35 @@ public class DesktopModeVisualIndicator {
return animator;
}
+ /** Calculates the bounds the indicator should have when fully faded in. */
private static Rect getIndicatorBounds(DisplayLayout layout, IndicatorType type) {
- final int padding = layout.stableInsets().top;
+ final Rect desktopStableBounds = new Rect();
+ layout.getStableBounds(desktopStableBounds);
+ final int padding = desktopStableBounds.top;
switch (type) {
case TO_FULLSCREEN_INDICATOR:
- return new Rect(padding, padding,
- layout.width() - padding,
- layout.height() - padding);
+ desktopStableBounds.top += padding;
+ desktopStableBounds.bottom -= padding;
+ desktopStableBounds.left += padding;
+ desktopStableBounds.right -= padding;
+ return desktopStableBounds;
case TO_DESKTOP_INDICATOR:
final float adjustmentPercentage = 1f
- DesktopTasksController.DESKTOP_MODE_INITIAL_BOUNDS_SCALE;
- return new Rect((int) (adjustmentPercentage * layout.width() / 2),
- (int) (adjustmentPercentage * layout.height() / 2),
- (int) (layout.width() - (adjustmentPercentage * layout.width() / 2)),
- (int) (layout.height() - (adjustmentPercentage * layout.height() / 2)));
+ return new Rect((int) (adjustmentPercentage * desktopStableBounds.width() / 2),
+ (int) (adjustmentPercentage * desktopStableBounds.height() / 2),
+ (int) (desktopStableBounds.width()
+ - (adjustmentPercentage * desktopStableBounds.width() / 2)),
+ (int) (desktopStableBounds.height()
+ - (adjustmentPercentage * desktopStableBounds.height() / 2)));
case TO_SPLIT_LEFT_INDICATOR:
return new Rect(padding, padding,
- layout.width() / 2 - padding,
- layout.height() - padding);
+ desktopStableBounds.width() / 2 - padding,
+ desktopStableBounds.height());
case TO_SPLIT_RIGHT_INDICATOR:
- return new Rect(layout.width() / 2 + padding, padding,
- layout.width() - padding,
- layout.height() - padding);
+ return new Rect(desktopStableBounds.width() / 2 + padding, padding,
+ desktopStableBounds.width() - padding,
+ desktopStableBounds.height());
default:
throw new IllegalArgumentException("Invalid indicator type provided.");
}
@@ -505,17 +513,18 @@ public class DesktopModeVisualIndicator {
}
/**
- * Return the max bounds of a visual indicator
+ * Return the minimum bounds of a visual indicator, to be used at the end of fading out
+ * and the start of fading in.
*/
- private static Rect getMaxBounds(Rect startBounds) {
- return new Rect((int) (startBounds.left
- - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())),
- (int) (startBounds.top
- - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())),
- (int) (startBounds.right
- + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())),
- (int) (startBounds.bottom
- + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())));
+ private static Rect getMinBounds(Rect maxBounds) {
+ return new Rect((int) (maxBounds.left
+ + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.width())),
+ (int) (maxBounds.top
+ + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.height())),
+ (int) (maxBounds.right
+ - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.width())),
+ (int) (maxBounds.bottom
+ - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.height())));
}
}
}
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 57a59c946f98..4440778a5a45 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
@@ -659,6 +659,7 @@ class DesktopTasksController(
}
val wct = WindowContainerTransaction()
+ if (!task.isFreeform) addMoveToDesktopChanges(wct, task, displayId)
wct.reparent(task.token, displayAreaInfo.token, true /* onTop */)
transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
@@ -1184,7 +1185,13 @@ class DesktopTasksController(
val options = createNewWindowOptions(callingTask)
if (options.launchWindowingMode == WINDOWING_MODE_FREEFORM) {
wct.startTask(requestedTaskId, options.toBundle())
- transitions.startTransition(TRANSIT_OPEN, wct, null)
+ val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
+ callingTask.displayId, wct, requestedTaskId)
+ val runOnTransit = immersiveTransitionHandler
+ .exitImmersiveIfApplicable(wct, callingTask.displayId)
+ val transition = transitions.startTransition(TRANSIT_OPEN, wct, null)
+ addPendingMinimizeTransition(transition, taskToMinimize)
+ runOnTransit?.invoke(transition)
} else {
val splitPosition = splitScreenController.determineNewInstancePosition(callingTask)
splitScreenController.startTask(requestedTaskId, splitPosition,
@@ -1218,7 +1225,8 @@ class DesktopTasksController(
.determineNewInstancePosition(callingTaskInfo)
splitScreenController.startIntent(
launchIntent, context.userId, fillIn, splitPosition,
- options.toBundle(), null /* hideTaskToken */
+ options.toBundle(), null /* hideTaskToken */,
+ true /* forceLaunchNewTask */
)
}
WINDOWING_MODE_FREEFORM -> {
@@ -1245,7 +1253,7 @@ class DesktopTasksController(
val bounds = when (newTaskWindowingMode) {
WINDOWING_MODE_FREEFORM -> {
displayController.getDisplayLayout(callingTask.displayId)
- ?.let { getInitialBounds(it, callingTask) }
+ ?.let { getInitialBounds(it, callingTask, callingTask.displayId) }
}
WINDOWING_MODE_MULTI_WINDOW -> {
Rect()
@@ -1311,7 +1319,7 @@ class DesktopTasksController(
val displayLayout = displayController.getDisplayLayout(task.displayId)
if (displayLayout != null) {
val initialBounds = Rect(task.configuration.windowConfiguration.bounds)
- cascadeWindow(task, initialBounds, displayLayout)
+ cascadeWindow(initialBounds, displayLayout, task.displayId)
wct.setBounds(task.token, initialBounds)
}
}
@@ -1399,13 +1407,19 @@ class DesktopTasksController(
return if (wct.isEmpty) null else wct
}
+ /**
+ * Apply all changes required when task is first added to desktop. Uses the task's current
+ * display by default to apply initial bounds and placement relative to the display.
+ * Use a different [displayId] if the task should be moved to a different display.
+ */
@VisibleForTesting
fun addMoveToDesktopChanges(
wct: WindowContainerTransaction,
- taskInfo: RunningTaskInfo
+ taskInfo: RunningTaskInfo,
+ displayId: Int = taskInfo.displayId,
) {
- val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
- val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
+ val displayLayout = displayController.getDisplayLayout(displayId) ?: return
+ val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)!!
val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
val targetWindowingMode =
if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) {
@@ -1414,7 +1428,7 @@ class DesktopTasksController(
} else {
WINDOWING_MODE_FREEFORM
}
- val initialBounds = getInitialBounds(displayLayout, taskInfo)
+ val initialBounds = getInitialBounds(displayLayout, taskInfo, displayId)
if (canChangeTaskPosition(taskInfo)) {
wct.setBounds(taskInfo.token, initialBounds)
@@ -1428,7 +1442,8 @@ class DesktopTasksController(
private fun getInitialBounds(
displayLayout: DisplayLayout,
- taskInfo: RunningTaskInfo
+ taskInfo: RunningTaskInfo,
+ displayId: Int,
): Rect {
val bounds = if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue) {
calculateInitialBounds(displayLayout, taskInfo)
@@ -1437,7 +1452,7 @@ class DesktopTasksController(
}
if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue) {
- cascadeWindow(taskInfo, bounds, displayLayout)
+ cascadeWindow(bounds, displayLayout, displayId)
}
return bounds
}
@@ -1466,11 +1481,11 @@ class DesktopTasksController(
}
}
- private fun cascadeWindow(task: TaskInfo, bounds: Rect, displayLayout: DisplayLayout) {
+ private fun cascadeWindow(bounds: Rect, displayLayout: DisplayLayout, displayId: Int) {
val stableBounds = Rect()
displayLayout.getStableBoundsForDesktopMode(stableBounds)
- val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(task.displayId)
+ val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(displayId)
activeTasks.firstOrNull()?.let { activeTask ->
shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let {
cascadeWindow(context.resources, stableBounds,
@@ -2069,6 +2084,12 @@ class DesktopTasksController(
c.removeDesktop(displayId)
}
}
+
+ override fun moveToExternalDisplay(taskId: Int) {
+ executeRemoteCallWithTaskPermission(controller, "moveTaskToExternalDisplay") { c ->
+ c.moveToNextDisplay(taskId)
+ }
+ }
}
private fun logV(msg: String, vararg arguments: Any?) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index 86351e364cdd..c27813de5358 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -52,4 +52,7 @@ interface IDesktopMode {
/** Remove desktop on the given display */
oneway void removeDesktop(int displayId);
+
+ /** Move a task with given `taskId` to external display */
+ void moveToExternalDisplay(int taskId);
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index cbb08b804dfe..6da39951efbe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -463,7 +463,7 @@ public class PipTransition extends PipTransitionController {
// so update fixed rotation state to default.
mFixedRotationState = FIXED_ROTATION_UNDEFINED;
- if (transition != mExitTransition) {
+ if (transition != mExitTransition && transition != mMoveToBackTransition) {
return;
}
// This means an expand happened before enter-pip finished and we are now "merging" a
@@ -477,8 +477,10 @@ public class PipTransition extends PipTransitionController {
cancelled = true;
}
- // Unset exitTransition AFTER cancel so that finishResize knows we are merging.
+ // Unset both exitTransition and moveToBackTransition AFTER cancel so that
+ // finishResize knows we are merging.
mExitTransition = null;
+ mMoveToBackTransition = null;
if (!cancelled) return;
final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo();
if (taskInfo != null) {
@@ -515,7 +517,8 @@ public class PipTransition extends PipTransitionController {
// means we're expecting the exit transition will be "merged" into another transition
// (likely a remote like launcher), so don't fire the finish-callback here -- wait until
// the exit transition is merged.
- if ((mExitTransition == null || isAnimatingLocally()) && mFinishCallback != null) {
+ if ((mExitTransition == null || mMoveToBackTransition == null || isAnimatingLocally())
+ && mFinishCallback != null) {
final SurfaceControl leash = mPipOrganizer.getSurfaceControl();
final boolean hasValidLeash = leash != null && leash.isValid();
WindowContainerTransaction wct = null;
@@ -665,17 +668,6 @@ public class PipTransition extends PipTransitionController {
return null;
}
- @Nullable
- private TransitionInfo.Change findFixedRotationChange(@NonNull TransitionInfo info) {
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (change.getEndFixedRotation() != ROTATION_UNDEFINED) {
- return change;
- }
- }
- return null;
- }
-
private void startExitAnimation(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 9b815817d4d3..94b344fb575a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.pip;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_PIP;
@@ -346,6 +347,21 @@ public abstract class PipTransitionController implements Transitions.TransitionH
return false;
}
+ /**
+ * Gets a change amongst the transition targets that is in a different final orientation than
+ * the display, signalling a potential fixed rotation transition.
+ */
+ @Nullable
+ public TransitionInfo.Change findFixedRotationChange(@NonNull TransitionInfo info) {
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getEndFixedRotation() != ROTATION_UNDEFINED) {
+ return change;
+ }
+ }
+ return null;
+ }
+
/** End the currently-playing PiP animation. */
public void end() {
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
index f40a87c39aef..fcd5c3baab5d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipEnterAnimator.java
@@ -16,10 +16,14 @@
package com.android.wm.shell.pip2.animation;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
import android.animation.Animator;
import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.Surface;
@@ -60,6 +64,11 @@ public class PipEnterAnimator extends ValueAnimator
private final PointF mInitScale = new PointF();
private final PointF mInitPos = new PointF();
private final Rect mInitCrop = new Rect();
+ private final PointF mInitActivityScale = new PointF();
+ private final PointF mInitActivityPos = new PointF();
+
+ Matrix mTransformTensor = new Matrix();
+ final float[] mMatrixTmp = new float[9];
public PipEnterAnimator(Context context,
@NonNull SurfaceControl leash,
@@ -109,6 +118,10 @@ public class PipEnterAnimator extends ValueAnimator
@Override
public void onAnimationEnd(@NonNull Animator animation) {
+ if (mFinishTransaction != null) {
+ onEnterAnimationUpdate(mInitScale, mInitPos, mInitCrop,
+ 1f /* fraction */, mFinishTransaction);
+ }
if (mAnimationEndCallback != null) {
mAnimationEndCallback.run();
}
@@ -126,16 +139,24 @@ public class PipEnterAnimator extends ValueAnimator
float fraction, SurfaceControl.Transaction tx) {
float scaleX = 1 + (initScale.x - 1) * (1 - fraction);
float scaleY = 1 + (initScale.y - 1) * (1 - fraction);
- tx.setScale(mLeash, scaleX, scaleY);
-
float posX = initPos.x + (mEndBounds.left - initPos.x) * fraction;
float posY = initPos.y + (mEndBounds.top - initPos.y) * fraction;
- tx.setPosition(mLeash, posX, posY);
+
+ int normalizedRotation = mRotation;
+ if (normalizedRotation == ROTATION_270) {
+ normalizedRotation = -ROTATION_90;
+ }
+ float degrees = -normalizedRotation * 90f * fraction;
Rect endCrop = new Rect(mEndBounds);
endCrop.offsetTo(0, 0);
mRectEvaluator.evaluate(fraction, initCrop, endCrop);
tx.setCrop(mLeash, mAnimatedRect);
+
+ mTransformTensor.setScale(scaleX, scaleY);
+ mTransformTensor.postTranslate(posX, posY);
+ mTransformTensor.postRotate(degrees);
+ tx.setMatrix(mLeash, mTransformTensor, mMatrixTmp);
}
// no-ops
@@ -153,7 +174,22 @@ public class PipEnterAnimator extends ValueAnimator
* calculated differently from generic transitions.
* @param pipChange PiP change received as a transition target.
*/
- public void setEnterStartState(@NonNull TransitionInfo.Change pipChange) {
+ public void setEnterStartState(@NonNull TransitionInfo.Change pipChange,
+ @NonNull TransitionInfo.Change pipActivityChange) {
+ PipUtils.calcEndTransform(pipActivityChange, pipChange, mInitActivityScale,
+ mInitActivityPos);
+ if (mStartTransaction != null && pipActivityChange.getLeash() != null) {
+ mStartTransaction.setCrop(pipActivityChange.getLeash(), null);
+ mStartTransaction.setScale(pipActivityChange.getLeash(), mInitActivityScale.x,
+ mInitActivityScale.y);
+ mStartTransaction.setPosition(pipActivityChange.getLeash(), mInitActivityPos.x,
+ mInitActivityPos.y);
+ mFinishTransaction.setCrop(pipActivityChange.getLeash(), null);
+ mFinishTransaction.setScale(pipActivityChange.getLeash(), mInitActivityScale.x,
+ mInitActivityScale.y);
+ mFinishTransaction.setPosition(pipActivityChange.getLeash(), mInitActivityPos.x,
+ mInitActivityPos.y);
+ }
PipUtils.calcStartTransform(pipChange, mInitScale, mInitPos, mInitCrop);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
index 8fa5aa933929..a93ef12cb7fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipExpandAnimator.java
@@ -157,6 +157,7 @@ public class PipExpandAnimator extends ValueAnimator
.shadow(tx, mLeash, false /* applyCornerRadius */);
tx.apply();
}
+
private Rect getInsets(float fraction) {
final Rect startInsets = mSourceRectHintInsets;
final Rect endInsets = mZeroInsets;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 73be8db0ea8a..0427294579dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -292,23 +292,34 @@ public class PipController implements ConfigurationChangeListener,
setDisplayLayout(mDisplayController.getDisplayLayout(displayId));
if (!mPipTransitionState.isInPip()) {
+ // Skip the PiP-relevant updates if we aren't in a valid PiP state.
+ if (mPipTransitionState.isInFixedRotation()) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Fixed rotation flag shouldn't be set while in an invalid PiP state");
+ }
return;
}
mPipTouchHandler.updateMinMaxSize(mPipBoundsState.getAspectRatio());
- // Update the caches to reflect the new display layout in the movement bounds;
- // temporarily update bounds to be at the top left for the movement bounds calculation.
- Rect toBounds = new Rect(0, 0,
- (int) Math.ceil(mPipBoundsState.getMaxSize().x * boundsScale),
- (int) Math.ceil(mPipBoundsState.getMaxSize().y * boundsScale));
- mPipBoundsState.setBounds(toBounds);
- mPipTouchHandler.updateMovementBounds();
-
- // The policy is to keep PiP snap fraction invariant.
- mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction);
- mPipBoundsState.setBounds(toBounds);
- t.setBounds(mPipTransitionState.mPipTaskToken, toBounds);
+ if (mPipTransitionState.isInFixedRotation()) {
+ // Do not change the bounds when in fixed rotation, but do update the movement bounds
+ // based on the current bounds state and potentially new display layout.
+ mPipTouchHandler.updateMovementBounds();
+ mPipTransitionState.setInFixedRotation(false);
+ } else {
+ Rect toBounds = new Rect(0, 0,
+ (int) Math.ceil(mPipBoundsState.getMaxSize().x * boundsScale),
+ (int) Math.ceil(mPipBoundsState.getMaxSize().y * boundsScale));
+ // Update the caches to reflect the new display layout in the movement bounds;
+ // temporarily update bounds to be at the top left for the movement bounds calculation.
+ mPipBoundsState.setBounds(toBounds);
+ mPipTouchHandler.updateMovementBounds();
+ // The policy is to keep PiP snap fraction invariant.
+ mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction);
+ mPipBoundsState.setBounds(toBounds);
+ }
+ t.setBounds(mPipTransitionState.mPipTaskToken, mPipBoundsState.getBounds());
}
private void setDisplayLayout(DisplayLayout layout) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index a4a7973ef4bb..4d0432e1066e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -406,12 +406,9 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
// We need to remove the callback even if the shelf is visible, in case it the delayed
// callback hasn't been executed yet to avoid the wrong final state.
mMainExecutor.removeCallbacks(mMoveOnShelVisibilityChanged);
- if (shelfVisible) {
- mMoveOnShelVisibilityChanged.run();
- } else {
- // Postpone moving in response to hide of Launcher in case there's another change
- mMainExecutor.executeDelayed(mMoveOnShelVisibilityChanged, PIP_KEEP_CLEAR_AREAS_DELAY);
- }
+
+ // Postpone moving in response to hide of Launcher in case there's another change
+ mMainExecutor.executeDelayed(mMoveOnShelVisibilityChanged, PIP_KEEP_CLEAR_AREAS_DELAY);
}
/**
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 ac1567aba6e9..779e4ea51347 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
@@ -16,7 +16,9 @@
package com.android.wm.shell.pip2.phone;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -36,6 +38,7 @@ import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.content.Context;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
@@ -388,8 +391,15 @@ public class PipTransition extends PipTransitionController implements
return false;
}
- Rect startBounds = pipChange.getStartAbsBounds();
+ // We expect the PiP activity as a separate change in a config-at-end transition.
+ TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
+ pipChange.getTaskInfo().getToken());
+ if (pipActivityChange == null) {
+ return false;
+ }
+
Rect endBounds = pipChange.getEndAbsBounds();
+ Rect activityEndBounds = pipActivityChange.getEndAbsBounds();
SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
Preconditions.checkNotNull(pipLeash, "Leash is null for bounds transition.");
@@ -411,14 +421,63 @@ public class PipTransition extends PipTransitionController implements
}
}
+ final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
+ int startRotation = pipChange.getStartRotation();
+ int endRotation = fixedRotationChange != null
+ ? fixedRotationChange.getEndFixedRotation() : ROTATION_UNDEFINED;
+ final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0
+ : startRotation - endRotation;
+
+ if (delta != ROTATION_0) {
+ mPipTransitionState.setInFixedRotation(true);
+ handleBoundsTypeFixedRotation(pipChange, pipActivityChange, fixedRotationChange);
+ }
+
PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash,
- startTransaction, finishTransaction, endBounds, sourceRectHint, Surface.ROTATION_0);
- animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange));
+ startTransaction, finishTransaction, endBounds, sourceRectHint, delta);
+ animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange,
+ pipActivityChange));
animator.setAnimationEndCallback(this::finishInner);
animator.start();
return true;
}
+ private void handleBoundsTypeFixedRotation(TransitionInfo.Change pipTaskChange,
+ TransitionInfo.Change pipActivityChange,
+ TransitionInfo.Change fixedRotationChange) {
+ final Rect endBounds = pipTaskChange.getEndAbsBounds();
+ final Rect endActivityBounds = pipActivityChange.getEndAbsBounds();
+ int startRotation = pipTaskChange.getStartRotation();
+ int endRotation = fixedRotationChange.getEndFixedRotation();
+
+ // Cache the task to activity offset to potentially restore later.
+ Point activityEndOffset = new Point(endActivityBounds.left - endBounds.left,
+ endActivityBounds.top - endBounds.top);
+
+ // If we are running a fixed rotation bounds enter PiP animation,
+ // then update the display layout rotation, and recalculate the end rotation bounds.
+ // Update the endBounds in place, so that the PiP change is up-to-date.
+ mPipDisplayLayoutState.rotateTo(endRotation);
+ float snapFraction = mPipBoundsAlgorithm.getSnapFraction(
+ mPipBoundsAlgorithm.getEntryDestinationBounds());
+ mPipBoundsAlgorithm.applySnapFraction(endBounds, snapFraction);
+ mPipBoundsState.setBounds(endBounds);
+
+ // Display bounds were already updated to represent the final orientation,
+ // so we just need to readjust the origin, and perform rotation about (0, 0).
+ boolean isClockwise = (endRotation - startRotation) == -ROTATION_270;
+ Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds();
+ int originTranslateX = isClockwise ? 0 : -displayBounds.width();
+ int originTranslateY = isClockwise ? -displayBounds.height() : 0;
+ endBounds.offset(originTranslateX, originTranslateY);
+
+ // Update the activity end bounds in place as well, as this is used for transform
+ // calculation later.
+ endActivityBounds.offsetTo(endBounds.left + activityEndOffset.x,
+ endBounds.top + activityEndOffset.y);
+ }
+
+
private boolean startAlphaTypeEnterAnimation(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@@ -533,6 +592,19 @@ public class PipTransition extends PipTransitionController implements
}
@Nullable
+ private TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info,
+ @NonNull WindowContainerToken parent) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (change.getTaskInfo() == null
+ && change.hasFlags(TransitionInfo.FLAG_CONFIG_AT_END)
+ && change.getParent() != null && change.getParent().equals(parent)) {
+ return change;
+ }
+ }
+ return null;
+ }
+
+ @Nullable
private TransitionInfo.Change getChangeByToken(TransitionInfo info,
WindowContainerToken token) {
for (TransitionInfo.Change change : info.getChanges()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
index a132796f4a84..ccdd66b5d1a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransitionState.java
@@ -155,6 +155,8 @@ public class PipTransitionState {
@Nullable
private Runnable mOnIdlePipTransitionStateRunnable;
+ private boolean mInFixedRotation = false;
+
/**
* An interface to track state updates as we progress through PiP transitions.
*/
@@ -256,7 +258,7 @@ public class PipTransitionState {
private void maybeRunOnIdlePipTransitionStateCallback() {
if (mOnIdlePipTransitionStateRunnable != null && isPipStateIdle()) {
- mOnIdlePipTransitionStateRunnable.run();
+ mMainHandler.post(mOnIdlePipTransitionStateRunnable);
mOnIdlePipTransitionStateRunnable = null;
}
}
@@ -303,6 +305,23 @@ public class PipTransitionState {
}
/**
+ * @return true if either in swipe or button-nav fixed rotation.
+ */
+ public boolean isInFixedRotation() {
+ return mInFixedRotation;
+ }
+
+ /**
+ * Sets the fixed rotation flag.
+ */
+ public void setInFixedRotation(boolean inFixedRotation) {
+ mInFixedRotation = inFixedRotation;
+ if (!inFixedRotation) {
+ maybeRunOnIdlePipTransitionStateCallback();
+ }
+ }
+
+ /**
* @return true if in swipe PiP to home. Note that this is true until overlay fades if used too.
*/
public boolean isInSwipePipToHomeTransition() {
@@ -351,7 +370,7 @@ public class PipTransitionState {
public boolean isPipStateIdle() {
// This needs to be a valid in-PiP state that isn't a transient state.
- return mState == ENTERED_PIP || mState == CHANGED_PIP_BOUNDS;
+ return (mState == ENTERED_PIP || mState == CHANGED_PIP_BOUNDS) && !isInFixedRotation();
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 9e39f440915c..a23b576beebc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -772,15 +772,25 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
instanceId);
}
+ @Override
+ public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent,
+ @SplitPosition int position, @Nullable Bundle options,
+ @Nullable WindowContainerToken hideTaskToken) {
+ startIntent(intent, userId1, fillInIntent, position, options, hideTaskToken,
+ false /* forceLaunchNewTask */);
+ }
+
/**
* Starts the given intent into split.
+ *
* @param hideTaskToken If non-null, a task matching this token will be moved to back in the
* same window container transaction as the starting of the intent.
+ * @param forceLaunchNewTask If true, this method will skip the check for a background task
+ * matching the intent and launch a new task.
*/
- @Override
public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options,
- @Nullable WindowContainerToken hideTaskToken) {
+ @Nullable WindowContainerToken hideTaskToken, boolean forceLaunchNewTask) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"startIntent(): intent=%s user=%d fillInIntent=%s position=%d", intent, userId1,
fillInIntent, position);
@@ -798,8 +808,9 @@ public class SplitScreenController implements SplitDragPolicy.Starter,
// To prevent accumulating large number of instances in the background, reuse task
// in the background. If we don't explicitly reuse, new may be created even if the app
// isn't multi-instance because WM won't automatically remove/reuse the previous instance
- final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
- .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1,
+ final ActivityManager.RecentTaskInfo taskInfo = forceLaunchNewTask ? null :
+ mRecentTasksOptional
+ .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1,
hideTaskToken))
.orElse(null);
if (taskInfo != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index fea987ac53e2..3e76403de51b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -175,6 +175,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private static final String TAG = StageCoordinator.class.getSimpleName();
+ // The duration in ms to prevent launch-adjacent from working after split screen is first
+ // entered
+ private static final int DISABLE_LAUNCH_ADJACENT_AFTER_ENTER_TIMEOUT_MS = 1000;
+
private final StageTaskListener mMainStage;
private final StageTaskListener mSideStage;
@SplitPosition
@@ -239,6 +243,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private SplitScreen.SplitInvocationListener mSplitInvocationListener;
private Executor mSplitInvocationListenerExecutor;
+ // Re-enables launch-adjacent handling on the split root task. This needs to be a member
+ // because we will be posting and removing it from the handler.
+ private final Runnable mReEnableLaunchAdjacentOnRoot = () -> setLaunchAdjacentDisabled(false);
+
/**
* Since StageCoordinator only coordinates MainStage and SideStage, it shouldn't support
* CompatUI layouts. CompatUI is handled separately by MainStage and SideStage.
@@ -2699,6 +2707,16 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
+ /**
+ * Sets whether launch-adjacent is disabled or enabled.
+ */
+ private void setLaunchAdjacentDisabled(boolean disabled) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setLaunchAdjacentDisabled: disabled=%b", disabled);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.setDisableLaunchAdjacent(mRootTaskInfo.token, disabled);
+ mTaskOrganizer.applyTransaction(wct);
+ }
+
/** Starts the pending transition animation. */
public boolean startPendingAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info,
@@ -2711,6 +2729,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mSplitTransitions.isPendingEnter(transition)) {
shouldAnimate = startPendingEnterAnimation(transition,
mSplitTransitions.mPendingEnter, info, startTransaction, finishTransaction);
+
+ // Disable launch adjacent after an enter animation to prevent cases where apps are
+ // incorrectly trampolining and incorrectly triggering a double launch-adjacent task
+ // launch (ie. main -> split -> main). See b/344216031
+ setLaunchAdjacentDisabled(true);
+ mMainHandler.removeCallbacks(mReEnableLaunchAdjacentOnRoot);
+ mMainHandler.postDelayed(mReEnableLaunchAdjacentOnRoot,
+ DISABLE_LAUNCH_ADJACENT_AFTER_ENTER_TIMEOUT_MS);
} else if (mSplitTransitions.isPendingDismiss(transition)) {
final SplitScreenTransitions.DismissSession dismiss = mSplitTransitions.mPendingDismiss;
shouldAnimate = startPendingDismissAnimation(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index ae4bd1615ae1..6313231b449e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -244,8 +244,9 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
return;
}
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
+ mVisible = taskInfo.isVisible && taskInfo.isVisibleRequested;
mCallbacks.onChildTaskStatusChanged(this, taskInfo.taskId, true /* present */,
- taskInfo.isVisible && taskInfo.isVisibleRequested);
+ mVisible);
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index f783b45589b3..5a2abe1e7e25 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -16,9 +16,9 @@
package com.android.wm.shell.unfold;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
@@ -30,6 +30,7 @@ import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -45,6 +46,8 @@ import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@@ -56,6 +59,18 @@ import java.util.concurrent.Executor;
*/
public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListener {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE,
+ DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD,
+ DefaultDisplayChange.DEFAULT_DISPLAY_FOLD,
+ })
+ private @interface DefaultDisplayChange {
+ int DEFAULT_DISPLAY_NO_CHANGE = 0;
+ int DEFAULT_DISPLAY_UNFOLD = 1;
+ int DEFAULT_DISPLAY_FOLD = 2;
+ }
+
private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
private final Transitions mTransitions;
private final Executor mExecutor;
@@ -66,7 +81,10 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
@Nullable
private IBinder mTransition;
+ // TODO: b/318803244 - remove when we could guarantee finishing the animation
+ // after startAnimation callback
private boolean mAnimationFinished = false;
+ private float mLastAnimationProgress = 0.0f;
private final List<UnfoldTaskAnimator> mAnimators = new ArrayList<>();
public UnfoldTransitionHandler(ShellInit shellInit,
@@ -107,16 +125,6 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull TransitionFinishCallback finishCallback) {
- if (shouldPlayUnfoldAnimation(info) && transition != mTransition) {
- // Take over transition that has unfold, we might receive it if no other handler
- // accepted request in handleRequest, e.g. for rotation + unfold or
- // TRANSIT_NONE + unfold transitions
- mTransition = transition;
-
- ProtoLog.v(WM_SHELL_TRANSITIONS, "UnfoldTransitionHandler: "
- + "take over startAnimation");
- }
-
if (transition != mTransition) return false;
for (int i = 0; i < mAnimators.size(); i++) {
@@ -158,6 +166,8 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
@Override
public void onStateChangeProgress(float progress) {
+ mLastAnimationProgress = progress;
+
if (mTransition == null) return;
SurfaceControl.Transaction transaction = null;
@@ -182,8 +192,14 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
@Override
public void onStateChangeFinished() {
- mAnimationFinished = true;
finishTransitionIfNeeded();
+
+ // mLastAnimationProgress is guaranteed to be 0f when folding finishes, see
+ // {@link PhysicsBasedUnfoldTransitionProgressProvider#cancelTransition}.
+ // We can use it as an indication that the next animation progress events will be related
+ // to unfolding, so let's reset mAnimationFinished to 'false' in this case.
+ final boolean isFoldingFinished = mLastAnimationProgress == 0f;
+ mAnimationFinished = !isFoldingFinished;
}
@Override
@@ -211,6 +227,12 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
// Apply changes happening during the unfold animation immediately
t.apply();
finishCallback.onTransitionFinished(null);
+
+ if (getDefaultDisplayChange(info) == DefaultDisplayChange.DEFAULT_DISPLAY_FOLD) {
+ // Force-finish current unfold animation as we are processing folding now which doesn't
+ // have any animations on the Shell side
+ finishTransitionIfNeeded();
+ }
}
/** Whether `request` contains an unfold action. */
@@ -219,18 +241,25 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
if (!ValueAnimator.areAnimatorsEnabled()) return false;
return (request.getType() == TRANSIT_CHANGE
- && request.getDisplayChange() != null
- && isUnfoldDisplayChange(request.getDisplayChange()));
+ && getDefaultDisplayChange(request.getDisplayChange())
+ == DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD);
}
- private boolean isUnfoldDisplayChange(
- @NonNull TransitionRequestInfo.DisplayChange displayChange) {
+ @DefaultDisplayChange
+ private int getDefaultDisplayChange(
+ @Nullable TransitionRequestInfo.DisplayChange displayChange) {
+ if (displayChange == null) return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
+
+ if (displayChange.getDisplayId() != DEFAULT_DISPLAY) {
+ return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
+ }
+
if (!displayChange.isPhysicalDisplayChanged()) {
- return false;
+ return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
}
if (displayChange.getStartAbsBounds() == null || displayChange.getEndAbsBounds() == null) {
- return false;
+ return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
}
// Handle only unfolding, currently we don't have an animation when folding
@@ -239,17 +268,11 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
final int startArea = displayChange.getStartAbsBounds().width()
* displayChange.getStartAbsBounds().height();
- return endArea > startArea;
+ return endArea > startArea ? DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD
+ : DefaultDisplayChange.DEFAULT_DISPLAY_FOLD;
}
- /** Whether `transitionInfo` contains an unfold action. */
- public boolean shouldPlayUnfoldAnimation(@NonNull TransitionInfo transitionInfo) {
- // Unfold animation won't play when animations are disabled
- if (!ValueAnimator.areAnimatorsEnabled()) return false;
- // Only handle transitions that are marked as physical display switch
- // See PhysicalDisplaySwitchTransitionLauncher for the conditions
- if ((transitionInfo.getFlags() & TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH) == 0) return false;
-
+ private int getDefaultDisplayChange(@NonNull TransitionInfo transitionInfo) {
for (int i = 0; i < transitionInfo.getChanges().size(); i++) {
final TransitionInfo.Change change = transitionInfo.getChanges().get(i);
// We are interested only in display container changes
@@ -268,11 +291,13 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
* change.getStartAbsBounds().height();
if (afterArea > beforeArea) {
- return true;
+ return DefaultDisplayChange.DEFAULT_DISPLAY_UNFOLD;
+ } else {
+ return DefaultDisplayChange.DEFAULT_DISPLAY_FOLD;
}
}
- return false;
+ return DefaultDisplayChange.DEFAULT_DISPLAY_NO_CHANGE;
}
@Nullable
@@ -293,10 +318,6 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene
@Override
public void onFoldStateChanged(boolean isFolded) {
if (isFolded) {
- // Reset unfold animation finished flag on folding, so it could be used next time
- // when we unfold the device as an indication that animation hasn't finished yet
- mAnimationFinished = false;
-
// If we are currently animating unfold animation we should finish it because
// the animation might not start and finish as the device was folded
finishTransitionIfNeeded();
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 2c621b1f1a52..d3b7ca15856f 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
@@ -514,8 +514,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
));
} else {
mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
- mTaskInfo, TaskInfoKt.getRequestingImmersive(mTaskInfo), inFullImmersive,
- hasGlobalFocus
+ mTaskInfo,
+ TaskInfoKt.getRequestingImmersive(mTaskInfo),
+ inFullImmersive,
+ hasGlobalFocus,
+ /* maximizeHoverEnabled= */ canOpenMaximizeMenu(
+ /* animatingTaskResizeOrReposition= */ false)
));
}
Trace.endSection();
@@ -1616,8 +1620,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
void setAnimatingTaskResizeOrReposition(boolean animatingTaskResizeOrReposition) {
if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) return;
- asAppHeader(mWindowDecorViewHolder)
- .setAnimatingTaskResizeOrReposition(animatingTaskResizeOrReposition);
+ final boolean inFullImmersive =
+ mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId);
+ asAppHeader(mWindowDecorViewHolder).bindData(new AppHeaderViewHolder.HeaderData(
+ mTaskInfo,
+ TaskInfoKt.getRequestingImmersive(mTaskInfo),
+ inFullImmersive,
+ isFocused(),
+ /* maximizeHoverEnabled= */ canOpenMaximizeMenu(animatingTaskResizeOrReposition)));
}
/**
@@ -1634,6 +1644,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
asAppHeader(mWindowDecorViewHolder).onMaximizeWindowHoverEnter();
}
+ private boolean canOpenMaximizeMenu(boolean animatingTaskResizeOrReposition) {
+ if (!Flags.enableFullyImmersiveInDesktop()) {
+ return !animatingTaskResizeOrReposition;
+ }
+ final boolean inImmersiveAndRequesting =
+ mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId)
+ && TaskInfoKt.getRequestingImmersive(mTaskInfo);
+ return !animatingTaskResizeOrReposition && !inImmersiveAndRequesting;
+ }
+
@Override
public String toString() {
return "{"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 0cb219ae4b81..3ae5a1afc7e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -62,6 +62,7 @@ import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.calculateMaximizeBounds
import com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_LINEAR_IN
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
@@ -73,7 +74,8 @@ import java.util.function.Supplier
/**
* Menu that appears when user long clicks the maximize button. Gives the user the option to
- * maximize the task or snap the task to the right or left half of the screen.
+ * maximize the task or restore previous task bounds from the maximized state and to snap the task
+ * to the right or left half of the screen.
*/
class MaximizeMenu(
private val syncQueue: SyncTransactionQueue,
@@ -176,6 +178,7 @@ class MaximizeMenu(
"MaximizeMenu")
maximizeMenuView = MaximizeMenuView(
context = decorWindowContext,
+ sizeToggleDirection = getSizeToggleDirection(),
menuHeight = menuHeight,
menuPadding = menuPadding,
).also { menuView ->
@@ -202,6 +205,18 @@ class MaximizeMenu(
}
}
+ private fun getSizeToggleDirection(): MaximizeMenuView.SizeToggleDirection {
+ val maximizeBounds = calculateMaximizeBounds(
+ displayController.getDisplayLayout(taskInfo.displayId)!!,
+ taskInfo
+ )
+ val maximized = taskInfo.configuration.windowConfiguration.bounds.equals(maximizeBounds)
+ return if (maximized)
+ MaximizeMenuView.SizeToggleDirection.RESTORE
+ else
+ MaximizeMenuView.SizeToggleDirection.MAXIMIZE
+ }
+
private fun loadDimensionPixelSize(resourceId: Int): Int {
return if (resourceId == Resources.ID_NULL) {
0
@@ -236,18 +251,19 @@ class MaximizeMenu(
* resizing a Task.
*/
class MaximizeMenuView(
- context: Context,
+ private val context: Context,
+ private val sizeToggleDirection: SizeToggleDirection,
private val menuHeight: Int,
- private val menuPadding: Int,
+ private val menuPadding: Int
) {
val rootView = LayoutInflater.from(context)
.inflate(R.layout.desktop_mode_window_decor_maximize_menu, null /* root */) as ViewGroup
private val container = requireViewById(R.id.container)
private val overlay = requireViewById(R.id.maximize_menu_overlay)
- private val maximizeText =
- requireViewById(R.id.maximize_menu_maximize_window_text) as TextView
- private val maximizeButton =
- requireViewById(R.id.maximize_menu_maximize_button) as Button
+ private val sizeToggleButtonText =
+ requireViewById(R.id.maximize_menu_size_toggle_button_text) as TextView
+ private val sizeToggleButton =
+ requireViewById(R.id.maximize_menu_size_toggle_button) as Button
private val snapWindowText =
requireViewById(R.id.maximize_menu_snap_window_text) as TextView
private val snapRightButton =
@@ -263,8 +279,6 @@ class MaximizeMenu(
.getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_outline_radius)
private val outlineStroke = context.resources
.getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_outline_stroke)
- private val fillPadding = context.resources
- .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_fill_padding)
private val fillRadius = context.resources
.getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_fill_radius)
@@ -324,7 +338,7 @@ class MaximizeMenu(
return@setOnHoverListener false
}
- maximizeButton.setOnClickListener { onMaximizeClickListener?.invoke() }
+ sizeToggleButton.setOnClickListener { onMaximizeClickListener?.invoke() }
snapRightButton.setOnClickListener { onRightSnapClickListener?.invoke() }
snapLeftButton.setOnClickListener { onLeftSnapClickListener?.invoke() }
rootView.setOnTouchListener { _, event ->
@@ -335,9 +349,17 @@ class MaximizeMenu(
true
}
+ val btnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE)
+ R.string.desktop_mode_maximize_menu_restore_button_text
+ else
+ R.string.desktop_mode_maximize_menu_maximize_button_text
+ val btnText = context.resources.getText(btnTextId)
+ sizeToggleButton.contentDescription = btnText
+ sizeToggleButtonText.text = btnText
+
// To prevent aliasing.
- maximizeButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
- maximizeText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
}
/** Bind the menu views to the new [RunningTaskInfo] data. */
@@ -348,8 +370,8 @@ class MaximizeMenu(
rootView.background.setTint(style.backgroundColor)
// Maximize option.
- maximizeButton.background = style.maximizeOption.drawable
- maximizeText.setTextColor(style.textColor)
+ sizeToggleButton.background = style.maximizeOption.drawable
+ sizeToggleButtonText.setTextColor(style.textColor)
// Snap options.
snapWindowText.setTextColor(style.textColor)
@@ -358,8 +380,8 @@ class MaximizeMenu(
/** Animate the opening of the menu */
fun animateOpenMenu(onEnd: () -> Unit) {
- maximizeButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
- maximizeText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ sizeToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ sizeToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
menuAnimatorSet = AnimatorSet()
menuAnimatorSet?.playTogether(
ObjectAnimator.ofFloat(rootView, SCALE_Y, STARTING_MENU_HEIGHT_SCALE, 1f)
@@ -388,9 +410,9 @@ class MaximizeMenu(
// Scale up the children of the maximize menu so that the menu
// scale is cancelled out and only the background is scaled.
val value = animatedValue as Float
- maximizeButton.scaleY = value
+ sizeToggleButton.scaleY = value
snapButtonsLayout.scaleY = value
- maximizeText.scaleY = value
+ sizeToggleButtonText.scaleY = value
snapWindowText.scaleY = value
}
},
@@ -409,9 +431,9 @@ class MaximizeMenu(
startDelay = CONTROLS_ALPHA_OPEN_MENU_ANIMATION_DELAY_MS
addUpdateListener {
val value = animatedValue as Float
- maximizeButton.alpha = value
+ sizeToggleButton.alpha = value
snapButtonsLayout.alpha = value
- maximizeText.alpha = value
+ sizeToggleButtonText.alpha = value
snapWindowText.alpha = value
}
},
@@ -423,8 +445,8 @@ class MaximizeMenu(
)
menuAnimatorSet?.addListener(
onEnd = {
- maximizeButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
- maximizeText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
onEnd.invoke()
}
)
@@ -433,8 +455,8 @@ class MaximizeMenu(
/** Animate the closing of the menu */
fun animateCloseMenu(onEnd: (() -> Unit)) {
- maximizeButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
- maximizeText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ sizeToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ sizeToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
cancelAnimation()
menuAnimatorSet = AnimatorSet()
menuAnimatorSet?.playTogether(
@@ -464,9 +486,9 @@ class MaximizeMenu(
// Scale up the children of the maximize menu so that the menu
// scale is cancelled out and only the background is scaled.
val value = animatedValue as Float
- maximizeButton.scaleY = value
+ sizeToggleButton.scaleY = value
snapButtonsLayout.scaleY = value
- maximizeText.scaleY = value
+ sizeToggleButtonText.scaleY = value
snapWindowText.scaleY = value
}
},
@@ -485,9 +507,9 @@ class MaximizeMenu(
duration = ALPHA_ANIMATION_DURATION_MS
addUpdateListener {
val value = animatedValue as Float
- maximizeButton.alpha = value
+ sizeToggleButton.alpha = value
snapButtonsLayout.alpha = value
- maximizeText.alpha = value
+ sizeToggleButtonText.alpha = value
snapWindowText.alpha = value
}
},
@@ -498,8 +520,8 @@ class MaximizeMenu(
)
menuAnimatorSet?.addListener(
onEnd = {
- maximizeButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
- maximizeText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
onEnd?.invoke()
}
)
@@ -509,8 +531,8 @@ class MaximizeMenu(
/** Request that the accessibility service focus on the menu. */
fun requestAccessibilityFocus() {
// Focus the first button in the menu by default.
- maximizeButton.post {
- maximizeButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
+ sizeToggleButton.post {
+ sizeToggleButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
}
}
@@ -685,15 +707,31 @@ class MaximizeMenu(
paint.color = strokeAndFillColor
paint.style = Paint.Style.FILL
})
+
+ val (horizontalFillPadding, verticalFillPadding) =
+ if (sizeToggleDirection == SizeToggleDirection.MAXIMIZE) {
+ context.resources.getDimensionPixelSize(R.dimen
+ .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding) to
+ context.resources.getDimensionPixelSize(R.dimen
+ .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding)
+ } else {
+ context.resources.getDimensionPixelSize(R.dimen
+ .desktop_mode_maximize_menu_restore_button_fill_horizontal_padding) to
+ context.resources.getDimensionPixelSize(R.dimen
+ .desktop_mode_maximize_menu_restore_button_fill_vertical_padding)
+ }
+
return LayerDrawable(layers.toTypedArray()).apply {
when (numberOfLayers) {
3 -> {
setLayerInset(1, outlineStroke)
- setLayerInset(2, fillPadding)
+ setLayerInset(2, horizontalFillPadding, verticalFillPadding,
+ horizontalFillPadding, verticalFillPadding)
}
4 -> {
setLayerInset(intArrayOf(1, 2), outlineStroke)
- setLayerInset(3, fillPadding)
+ setLayerInset(3, horizontalFillPadding, verticalFillPadding,
+ horizontalFillPadding, verticalFillPadding)
}
else -> error("Unexpected number of layers: $numberOfLayers")
}
@@ -737,6 +775,11 @@ class MaximizeMenu(
enum class SnapToHalfSelection {
NONE, LEFT, RIGHT
}
+
+ /** The possible selection states of the size toggle button in the maximize menu. */
+ enum class SizeToggleDirection {
+ MAXIMIZE, RESTORE
+ }
}
companion object {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
index cf03b3f74dc7..d94391820d05 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
@@ -70,7 +70,7 @@ class AppHeaderViewHolder(
rootView: View,
onCaptionTouchListener: View.OnTouchListener,
onCaptionButtonClickListener: View.OnClickListener,
- onLongClickListener: OnLongClickListener,
+ private val onLongClickListener: OnLongClickListener,
onCaptionGenericMotionListener: View.OnGenericMotionListener,
appName: CharSequence,
appIconBitmap: Bitmap,
@@ -81,7 +81,8 @@ class AppHeaderViewHolder(
val taskInfo: RunningTaskInfo,
val isRequestingImmersive: Boolean,
val inFullImmersiveState: Boolean,
- val hasGlobalFocus: Boolean
+ val hasGlobalFocus: Boolean,
+ val enableMaximizeLongClick: Boolean,
) : Data()
private val decorThemeUtil = DecorThemeUtil(context)
@@ -160,19 +161,30 @@ class AppHeaderViewHolder(
}
override fun bindData(data: HeaderData) {
- bindData(data.taskInfo, data.isRequestingImmersive, data.inFullImmersiveState,
- data.hasGlobalFocus)
+ bindData(
+ data.taskInfo,
+ data.isRequestingImmersive,
+ data.inFullImmersiveState,
+ data.hasGlobalFocus,
+ data.enableMaximizeLongClick
+ )
}
private fun bindData(
taskInfo: RunningTaskInfo,
isRequestingImmersive: Boolean,
inFullImmersiveState: Boolean,
- hasGlobalFocus: Boolean
+ hasGlobalFocus: Boolean,
+ enableMaximizeLongClick: Boolean,
) {
if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue()) {
- bindDataWithThemedHeaders(taskInfo, isRequestingImmersive, inFullImmersiveState,
- hasGlobalFocus)
+ bindDataWithThemedHeaders(
+ taskInfo,
+ isRequestingImmersive,
+ inFullImmersiveState,
+ hasGlobalFocus,
+ enableMaximizeLongClick,
+ )
} else {
bindDataLegacy(taskInfo, hasGlobalFocus)
}
@@ -215,7 +227,8 @@ class AppHeaderViewHolder(
taskInfo: RunningTaskInfo,
requestingImmersive: Boolean,
inFullImmersiveState: Boolean,
- hasGlobalFocus: Boolean
+ hasGlobalFocus: Boolean,
+ enableMaximizeLongClick: Boolean,
) {
val header = fillHeaderInfo(taskInfo, hasGlobalFocus)
val headerStyle = getHeaderStyle(header)
@@ -281,6 +294,16 @@ class AppHeaderViewHolder(
drawableInsets = closeDrawableInsets
)
}
+ if (!enableMaximizeLongClick) {
+ maximizeButtonView.cancelHoverAnimation()
+ }
+ maximizeButtonView.hoverDisabled = !enableMaximizeLongClick
+ maximizeWindowButton.onLongClickListener = if (enableMaximizeLongClick) {
+ onLongClickListener
+ } else {
+ // Disable long-click to open maximize menu when in immersive.
+ null
+ }
}
override fun onHandleMenuOpened() {}
@@ -291,14 +314,6 @@ class AppHeaderViewHolder(
}
}
- fun setAnimatingTaskResizeOrReposition(animatingTaskResizeOrReposition: Boolean) {
- // If animating a task resize or reposition, cancel any running hover animations
- if (animatingTaskResizeOrReposition) {
- maximizeButtonView.cancelHoverAnimation()
- }
- maximizeButtonView.hoverDisabled = animatingTaskResizeOrReposition
- }
-
fun onMaximizeWindowHoverExit() {
maximizeButtonView.cancelHoverAnimation()
}
@@ -364,6 +379,11 @@ class AppHeaderViewHolder(
private fun shouldShowExitFullImmersiveIcon(
requestingImmersive: Boolean,
inFullImmersiveState: Boolean
+ ): Boolean = isInFullImmersiveStateAndRequesting(requestingImmersive, inFullImmersiveState)
+
+ private fun isInFullImmersiveStateAndRequesting(
+ requestingImmersive: Boolean,
+ inFullImmersiveState: Boolean
): Boolean = Flags.enableFullyImmersiveInDesktop()
&& requestingImmersive && inFullImmersiveState
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
index 094af9652ea3..a1d4a1a301bd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleOverflowTest.java
@@ -26,6 +26,7 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.wm.shell.ShellTestCase;
import org.junit.Before;
@@ -45,6 +46,7 @@ public class BubbleOverflowTest extends ShellTestCase {
private TestableBubblePositioner mPositioner;
private BubbleOverflow mOverflow;
private BubbleExpandedViewManager mExpandedViewManager;
+ private BubbleLogger mBubbleLogger;
@Mock
private BubbleController mBubbleController;
@@ -58,6 +60,7 @@ public class BubbleOverflowTest extends ShellTestCase {
mExpandedViewManager = BubbleExpandedViewManager.fromBubbleController(mBubbleController);
mPositioner = new TestableBubblePositioner(mContext,
mContext.getSystemService(WindowManager.class));
+ mBubbleLogger = new BubbleLogger(new UiEventLoggerFake());
when(mBubbleController.getPositioner()).thenReturn(mPositioner);
when(mBubbleController.getStackView()).thenReturn(mBubbleStackView);
@@ -77,7 +80,7 @@ public class BubbleOverflowTest extends ShellTestCase {
@Test
public void test_initialize_forBubbleBar() {
- mOverflow.initializeForBubbleBar(mExpandedViewManager, mPositioner);
+ mOverflow.initializeForBubbleBar(mExpandedViewManager, mPositioner, mBubbleLogger);
assertThat(mOverflow.getBubbleBarExpandedView()).isNotNull();
assertThat(mOverflow.getExpandedView()).isNull();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index dde9fda13ea9..0825b6b0d7be 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -18,7 +18,10 @@ package com.android.wm.shell.desktopmode
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
+import com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations
+import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker
import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions
import com.android.internal.util.FrameworkStatsLog
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.window.flags.Flags
@@ -26,15 +29,16 @@ import com.android.wm.shell.EventLogTags
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.NO_SESSION_ID
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskSizeUpdate
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_MINIMIZE_REASON
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_UNMINIMIZE_REASON
-import kotlinx.coroutines.runBlocking
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
+import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.mockito.kotlin.eq
@@ -49,40 +53,87 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
@JvmField
@Rule(order = 0)
val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
- .mockStatic(FrameworkStatsLog::class.java)
- .mockStatic(EventLogTags::class.java).build()!!
+ .mockStatic(FrameworkStatsLog::class.java)
+ .mockStatic(EventLogTags::class.java).build()!!
@JvmField
@Rule(order = 1)
val setFlagsRule = SetFlagsRule()
@Test
- fun logSessionEnter_enterReason() = runBlocking {
- desktopModeEventLogger.logSessionEnter(sessionId = SESSION_ID, EnterReason.UNKNOWN_ENTER)
+ fun logSessionEnter_logsEnterReasonWithNewSessionId() {
+ desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER)
+
+ val sessionId = desktopModeEventLogger.currentSessionId.get()
+ assertThat(sessionId).isNotEqualTo(NO_SESSION_ID)
+ verify {
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED),
+ /* event */
+ eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER),
+ /* enter_reason */
+ eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER),
+ /* exit_reason */
+ eq(0),
+ /* sessionId */
+ eq(sessionId)
+ )
+ }
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+ verify {
+ EventLogTags.writeWmShellEnterDesktopMode(
+ eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason),
+ eq(sessionId)
+ )
+ }
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ }
+
+ @Test
+ fun logSessionEnter_ongoingSession_logsEnterReasonWithNewSessionId() {
+ val previousSessionId = startDesktopModeSession()
+ desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER)
+
+ val sessionId = desktopModeEventLogger.currentSessionId.get()
+ assertThat(sessionId).isNotEqualTo(NO_SESSION_ID)
+ assertThat(sessionId).isNotEqualTo(previousSessionId)
verify {
FrameworkStatsLog.write(
eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED),
/* event */
eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER),
/* enter_reason */
- eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER),
+ eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER),
/* exit_reason */
eq(0),
/* sessionId */
- eq(SESSION_ID)
+ eq(sessionId)
)
}
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verify {
EventLogTags.writeWmShellEnterDesktopMode(
- eq(EnterReason.UNKNOWN_ENTER.reason),
- eq(SESSION_ID))
+ eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason),
+ eq(sessionId)
+ )
}
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
- fun logSessionExit_exitReason() = runBlocking {
- desktopModeEventLogger.logSessionExit(sessionId = SESSION_ID, ExitReason.UNKNOWN_EXIT)
+ fun logSessionExit_noOngoingSession_doesNotLog() {
+ desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT)
+
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ }
+
+ @Test
+ fun logSessionExit_logsExitReasonAndClearsSessionId() {
+ val sessionId = startDesktopModeSession()
+
+ desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT)
verify {
FrameworkStatsLog.write(
@@ -92,24 +143,39 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* enter_reason */
eq(0),
/* exit_reason */
- eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT),
+ eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT),
/* sessionId */
- eq(SESSION_ID)
+ eq(sessionId)
)
}
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verify {
EventLogTags.writeWmShellExitDesktopMode(
- eq(ExitReason.UNKNOWN_EXIT.reason),
- eq(SESSION_ID))
+ eq(ExitReason.DRAG_TO_EXIT.reason),
+ eq(sessionId)
+ )
}
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ assertThat(desktopModeEventLogger.currentSessionId.get()).isEqualTo(NO_SESSION_ID)
+ }
+
+ @Test
+ fun logTaskAdded_noOngoingSession_doesNotLog() {
+ desktopModeEventLogger.logTaskAdded(TASK_UPDATE)
+
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
- fun logTaskAdded_taskUpdate() = runBlocking {
- desktopModeEventLogger.logTaskAdded(sessionId = SESSION_ID, TASK_UPDATE)
+ fun logTaskAdded_logsTaskUpdate() {
+ val sessionId = startDesktopModeSession()
+
+ desktopModeEventLogger.logTaskAdded(TASK_UPDATE)
verify {
- FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
/* task_event */
eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED),
/* instance_id */
@@ -125,36 +191,52 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* task_y */
eq(TASK_UPDATE.taskY),
/* session_id */
- eq(SESSION_ID),
+ eq(sessionId),
eq(UNSET_MINIMIZE_REASON),
eq(UNSET_UNMINIMIZE_REASON),
/* visible_task_count */
- eq(TASK_COUNT))
+ eq(TASK_COUNT)
+ )
}
-
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verify {
EventLogTags.writeWmShellDesktopModeTaskUpdate(
- eq(FrameworkStatsLog
- .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED
+ ),
eq(TASK_UPDATE.instanceId),
eq(TASK_UPDATE.uid),
eq(TASK_UPDATE.taskHeight),
eq(TASK_UPDATE.taskWidth),
eq(TASK_UPDATE.taskX),
eq(TASK_UPDATE.taskY),
- eq(SESSION_ID),
+ eq(sessionId),
eq(UNSET_MINIMIZE_REASON),
eq(UNSET_UNMINIMIZE_REASON),
- eq(TASK_COUNT))
+ eq(TASK_COUNT)
+ )
}
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ }
+
+ @Test
+ fun logTaskRemoved_noOngoingSession_doesNotLog() {
+ desktopModeEventLogger.logTaskRemoved(TASK_UPDATE)
+
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
- fun logTaskRemoved_taskUpdate() = runBlocking {
- desktopModeEventLogger.logTaskRemoved(sessionId = SESSION_ID, TASK_UPDATE)
+ fun logTaskRemoved_taskUpdate() {
+ val sessionId = startDesktopModeSession()
+
+ desktopModeEventLogger.logTaskRemoved(TASK_UPDATE)
verify {
- FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
/* task_event */
eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED),
/* instance_id */
@@ -170,39 +252,57 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* task_y */
eq(TASK_UPDATE.taskY),
/* session_id */
- eq(SESSION_ID),
+ eq(sessionId),
eq(UNSET_MINIMIZE_REASON),
eq(UNSET_UNMINIMIZE_REASON),
/* visible_task_count */
- eq(TASK_COUNT))
+ eq(TASK_COUNT)
+ )
}
-
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verify {
EventLogTags.writeWmShellDesktopModeTaskUpdate(
- eq(FrameworkStatsLog
- .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED
+ ),
eq(TASK_UPDATE.instanceId),
eq(TASK_UPDATE.uid),
eq(TASK_UPDATE.taskHeight),
eq(TASK_UPDATE.taskWidth),
eq(TASK_UPDATE.taskX),
eq(TASK_UPDATE.taskY),
- eq(SESSION_ID),
+ eq(sessionId),
eq(UNSET_MINIMIZE_REASON),
eq(UNSET_UNMINIMIZE_REASON),
- eq(TASK_COUNT))
+ eq(TASK_COUNT)
+ )
}
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ }
+
+ @Test
+ fun logTaskInfoChanged_noOngoingSession_doesNotLog() {
+ desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE)
+
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
- fun logTaskInfoChanged_taskUpdate() = runBlocking {
- desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, TASK_UPDATE)
+ fun logTaskInfoChanged_taskUpdate() {
+ val sessionId = startDesktopModeSession()
+
+ desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE)
verify {
- FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
/* task_event */
- eq(FrameworkStatsLog
- .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+ ),
/* instance_id */
eq(TASK_UPDATE.instanceId),
/* uid */
@@ -216,40 +316,51 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* task_y */
eq(TASK_UPDATE.taskY),
/* session_id */
- eq(SESSION_ID),
+ eq(sessionId),
eq(UNSET_MINIMIZE_REASON),
eq(UNSET_UNMINIMIZE_REASON),
/* visible_task_count */
- eq(TASK_COUNT))
+ eq(TASK_COUNT)
+ )
}
-
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verify {
EventLogTags.writeWmShellDesktopModeTaskUpdate(
- eq(FrameworkStatsLog
- .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+ ),
eq(TASK_UPDATE.instanceId),
eq(TASK_UPDATE.uid),
eq(TASK_UPDATE.taskHeight),
eq(TASK_UPDATE.taskWidth),
eq(TASK_UPDATE.taskX),
eq(TASK_UPDATE.taskY),
- eq(SESSION_ID),
+ eq(sessionId),
eq(UNSET_MINIMIZE_REASON),
eq(UNSET_UNMINIMIZE_REASON),
- eq(TASK_COUNT))
+ eq(TASK_COUNT)
+ )
}
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
- fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() = runBlocking {
- desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID,
- createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT))
+ fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() {
+ val sessionId = startDesktopModeSession()
+
+ desktopModeEventLogger.logTaskInfoChanged(
+ createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT)
+ )
verify {
- FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
/* task_event */
- eq(FrameworkStatsLog
- .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+ ),
/* instance_id */
eq(TASK_UPDATE.instanceId),
/* uid */
@@ -263,42 +374,53 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* task_y */
eq(TASK_UPDATE.taskY),
/* session_id */
- eq(SESSION_ID),
+ eq(sessionId),
/* minimize_reason */
eq(MinimizeReason.TASK_LIMIT.reason),
/* unminimize_reason */
eq(UNSET_UNMINIMIZE_REASON),
/* visible_task_count */
- eq(TASK_COUNT))
+ eq(TASK_COUNT)
+ )
}
-
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verify {
EventLogTags.writeWmShellDesktopModeTaskUpdate(
- eq(FrameworkStatsLog
- .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+ ),
eq(TASK_UPDATE.instanceId),
eq(TASK_UPDATE.uid),
eq(TASK_UPDATE.taskHeight),
eq(TASK_UPDATE.taskWidth),
eq(TASK_UPDATE.taskX),
eq(TASK_UPDATE.taskY),
- eq(SESSION_ID),
+ eq(sessionId),
eq(MinimizeReason.TASK_LIMIT.reason),
eq(UNSET_UNMINIMIZE_REASON),
- eq(TASK_COUNT))
+ eq(TASK_COUNT)
+ )
}
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
- fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() = runBlocking {
- desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID,
- createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP))
+ fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() {
+ val sessionId = startDesktopModeSession()
+
+ desktopModeEventLogger.logTaskInfoChanged(
+ createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP)
+ )
verify {
- FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
/* task_event */
- eq(FrameworkStatsLog
- .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+ ),
/* instance_id */
eq(TASK_UPDATE.instanceId),
/* uid */
@@ -312,39 +434,55 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* task_y */
eq(TASK_UPDATE.taskY),
/* session_id */
- eq(SESSION_ID),
+ eq(sessionId),
/* minimize_reason */
eq(UNSET_MINIMIZE_REASON),
/* unminimize_reason */
eq(UnminimizeReason.TASKBAR_TAP.reason),
/* visible_task_count */
- eq(TASK_COUNT))
+ eq(TASK_COUNT)
+ )
}
-
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
verify {
EventLogTags.writeWmShellDesktopModeTaskUpdate(
- eq(FrameworkStatsLog
- .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+ eq(
+ FrameworkStatsLog
+ .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED
+ ),
eq(TASK_UPDATE.instanceId),
eq(TASK_UPDATE.uid),
eq(TASK_UPDATE.taskHeight),
eq(TASK_UPDATE.taskWidth),
eq(TASK_UPDATE.taskX),
eq(TASK_UPDATE.taskY),
- eq(SESSION_ID),
+ eq(sessionId),
eq(UNSET_MINIMIZE_REASON),
eq(UnminimizeReason.TASKBAR_TAP.reason),
- eq(TASK_COUNT))
+ eq(TASK_COUNT)
+ )
}
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
+ }
+
+ @Test
+ fun logTaskResizingStarted_noOngoingSession_doesNotLog() {
+ desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE)
+
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS)
- fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() = runBlocking {
- desktopModeEventLogger.logTaskResizingStarted(sessionId = SESSION_ID, createTaskSizeUpdate())
+ fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() {
+ val sessionId = startDesktopModeSession()
+
+ desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE)
verify {
- FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
/* resize_trigger */
eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER),
/* resizing_stage */
@@ -352,7 +490,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* input_method */
eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD),
/* desktop_mode_session_id */
- eq(SESSION_ID),
+ eq(sessionId),
/* instance_id */
eq(TASK_SIZE_UPDATE.instanceId),
/* uid */
@@ -365,15 +503,27 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(TASK_SIZE_UPDATE.displayArea),
)
}
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+ }
+
+ @Test
+ fun logTaskResizingEnded_noOngoingSession_doesNotLog() {
+ desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE)
+
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+ verifyZeroInteractions(staticMockMarker(EventLogTags::class.java))
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS)
- fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() = runBlocking {
- desktopModeEventLogger.logTaskResizingEnded(sessionId = SESSION_ID, createTaskSizeUpdate())
+ fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() {
+ val sessionId = startDesktopModeSession()
+
+ desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE)
verify {
- FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED),
/* resize_trigger */
eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER),
/* resizing_stage */
@@ -381,7 +531,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
/* input_method */
eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD),
/* desktop_mode_session_id */
- eq(SESSION_ID),
+ eq(sessionId),
/* instance_id */
eq(TASK_SIZE_UPDATE.instanceId),
/* uid */
@@ -394,6 +544,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
eq(TASK_SIZE_UPDATE.displayArea),
)
}
+ verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java))
+ }
+
+ private fun startDesktopModeSession(): Int {
+ desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER)
+ clearInvocations(staticMockMarker(FrameworkStatsLog::class.java))
+ clearInvocations(staticMockMarker(EventLogTags::class.java))
+ return desktopModeEventLogger.currentSessionId.get()
}
@Test
@@ -428,7 +586,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
}
private companion object {
- private const val SESSION_ID = 1
+ private const val sessionId = 1
private const val TASK_ID = 1
private const val TASK_UID = 1
private const val TASK_X = 0
@@ -453,23 +611,12 @@ class DesktopModeEventLoggerTest : ShellTestCase() {
DISPLAY_AREA,
)
- private fun createTaskSizeUpdate(
- resizeTrigger: ResizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER,
- inputMethod: InputMethod = InputMethod.UNKNOWN_INPUT_METHOD,
- ) = TaskSizeUpdate(
- resizeTrigger,
- inputMethod,
- TASK_ID,
- TASK_UID,
- TASK_HEIGHT,
- TASK_WIDTH,
- DISPLAY_AREA,
- )
-
private fun createTaskUpdate(
minimizeReason: MinimizeReason? = null,
unminimizeReason: UnminimizeReason? = null,
- ) = TaskUpdate(TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason,
- unminimizeReason, TASK_COUNT)
+ ) = TaskUpdate(
+ TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason,
+ unminimizeReason, TASK_COUNT
+ )
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index e7593b5b9324..f25faa53f356 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -59,8 +59,8 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
-import junit.framework.Assert.assertNotNull
-import junit.framework.Assert.assertNull
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -142,8 +142,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
callOnTransitionReady(transitionInfo)
- verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
- verify(desktopModeEventLogger, never()).logTaskAdded(any(), any())
+ verify(desktopModeEventLogger, never()).logSessionEnter(any())
+ verify(desktopModeEventLogger, never()).logTaskAdded(any())
}
@Test
@@ -228,11 +228,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
@Test
fun transitToFront_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
// previous exit to overview transition
- val previousSessionId = 1
// add a freeform task
val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
- transitionObserver.setLoggerSessionId(previousSessionId)
+ transitionObserver.isSessionActive = true
val previousTransitionInfo =
TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
.addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
@@ -241,7 +240,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
callOnTransitionReady(previousTransitionInfo)
verifyTaskRemovedAndExitLogging(
- previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+ ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+ )
// Enter desktop mode from cancelled recents has no transition. Enter is detected on the
// next transition involving freeform windows
@@ -259,11 +259,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
@Test
fun transitChange_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
// previous exit to overview transition
- val previousSessionId = 1
// add a freeform task
val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
- transitionObserver.setLoggerSessionId(previousSessionId)
+ transitionObserver.isSessionActive = true
val previousTransitionInfo =
TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
.addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
@@ -272,7 +271,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
callOnTransitionReady(previousTransitionInfo)
verifyTaskRemovedAndExitLogging(
- previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+ ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+ )
// Enter desktop mode from cancelled recents has no transition. Enter is detected on the
// next transition involving freeform windows
@@ -290,11 +290,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
@Test
fun transitOpen_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
// previous exit to overview transition
- val previousSessionId = 1
// add a freeform task
val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
- transitionObserver.setLoggerSessionId(previousSessionId)
+ transitionObserver.isSessionActive = true
val previousTransitionInfo =
TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
.addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
@@ -303,7 +302,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
callOnTransitionReady(previousTransitionInfo)
verifyTaskRemovedAndExitLogging(
- previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+ ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+ )
// Enter desktop mode from cancelled recents has no transition. Enter is detected on the
// next transition involving freeform windows
@@ -324,11 +324,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
// Tests for AppFromOverview precedence in compared to cancelled Overview
// previous exit to overview transition
- val previousSessionId = 1
// add a freeform task
val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
transitionObserver.addTaskInfosToCachedMap(previousTaskInfo)
- transitionObserver.setLoggerSessionId(previousSessionId)
+ transitionObserver.isSessionActive = true
val previousTransitionInfo =
TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
.addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo))
@@ -337,7 +336,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
callOnTransitionReady(previousTransitionInfo)
verifyTaskRemovedAndExitLogging(
- previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+ ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+ )
// TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM))
@@ -379,11 +379,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
fun transitBack_previousExitReasonScreenOff_logTaskAddedAndEnterReasonScreenOn() {
val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
// Previous Exit reason recorded as Screen Off
- val sessionId = 1
transitionObserver.addTaskInfosToCachedMap(freeformTask)
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
- verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+ verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
// Enter desktop through back transition, this happens when user enters after dismissing
// keyguard
val change = createChange(TRANSIT_TO_FRONT, freeformTask)
@@ -399,11 +398,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
fun transitEndDragToDesktop_previousExitReasonScreenOff_logTaskAddedAndEnterReasonAppDrag() {
val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM)
// Previous Exit reason recorded as Screen Off
- val sessionId = 1
transitionObserver.addTaskInfosToCachedMap(freeformTask)
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
- verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+ verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
// Enter desktop through app handle drag. This represents cases where instead of moving to
// desktop right after turning the screen on, we move to fullscreen then move another task
@@ -419,24 +417,22 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
}
@Test
- fun transitSleep_logTaskRemovedAndExitReasonScreenOff_sessionIdNull() {
- val sessionId = 1
+ fun transitSleep_logTaskRemovedAndExitReasonScreenOff() {
// add a freeform task
transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build()
callOnTransitionReady(transitionInfo)
- verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
+ verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE)
}
@Test
- fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() {
- val sessionId = 1
+ fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit() {
// add a freeform task
transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
// window mode changing from FREEFORM to FULLSCREEN
val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
@@ -444,15 +440,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build()
callOnTransitionReady(transitionInfo)
- verifyTaskRemovedAndExitLogging(sessionId, ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE)
+ verifyTaskRemovedAndExitLogging(ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE)
}
@Test
- fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() {
- val sessionId = 1
+ fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton() {
// add a freeform task
transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
// window mode changing from FREEFORM to FULLSCREEN
val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
@@ -462,16 +457,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
.build()
callOnTransitionReady(transitionInfo)
- verifyTaskRemovedAndExitLogging(
- sessionId, ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE)
+ verifyTaskRemovedAndExitLogging(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE)
}
@Test
- fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() {
- val sessionId = 1
+ fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard() {
// add a freeform task
transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
// window mode changing from FREEFORM to FULLSCREEN
val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
@@ -479,16 +472,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build()
callOnTransitionReady(transitionInfo)
- verifyTaskRemovedAndExitLogging(
- sessionId, ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE)
+ verifyTaskRemovedAndExitLogging(ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE)
}
@Test
- fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() {
- val sessionId = 1
+ fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown() {
// add a freeform task
transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
// window mode changing from FREEFORM to FULLSCREEN
val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
@@ -496,15 +487,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build()
callOnTransitionReady(transitionInfo)
- verifyTaskRemovedAndExitLogging(sessionId, ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE)
+ verifyTaskRemovedAndExitLogging(ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE)
}
@Test
- fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() {
- val sessionId = 1
+ fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview() {
// add a freeform task
transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
// recents transition
val change = createChange(TRANSIT_TO_BACK, createTaskInfo(WINDOWING_MODE_FREEFORM))
@@ -513,31 +503,30 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
callOnTransitionReady(transitionInfo)
verifyTaskRemovedAndExitLogging(
- sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+ ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+ )
}
@Test
- fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() {
- val sessionId = 1
+ fun transitClose_logTaskRemovedAndExitReasonTaskFinished() {
// add a freeform task
transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
// task closing
val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FULLSCREEN))
val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build()
callOnTransitionReady(transitionInfo)
- verifyTaskRemovedAndExitLogging(sessionId, ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE)
+ verifyTaskRemovedAndExitLogging(ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE)
}
@Test
fun sessionExitByRecents_cancelledAnimation_sessionRestored() {
- val sessionId = 1
// add a freeform task to an existing session
val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
transitionObserver.addTaskInfosToCachedMap(taskInfo)
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
// recents transition sent freeform window to back
val change = createChange(TRANSIT_TO_BACK, taskInfo)
@@ -546,7 +535,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
callOnTransitionReady(transitionInfo1)
verifyTaskRemovedAndExitLogging(
- sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE)
+ ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE
+ )
val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
callOnTransitionReady(transitionInfo2)
@@ -557,10 +547,9 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
@Test
fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() {
- val sessionId = 1
// add an existing freeform task
transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
// new freeform task added
val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
@@ -568,18 +557,16 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
callOnTransitionReady(transitionInfo)
verify(desktopModeEventLogger, times(1))
- .logTaskAdded(eq(sessionId),
- eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2)))
- verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
+ .logTaskAdded(eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2)))
+ verify(desktopModeEventLogger, never()).logSessionEnter(any())
}
@Test
fun sessionAlreadyStarted_taskPositionChanged_logsTaskUpdate() {
- val sessionId = 1
// add an existing freeform task
val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
transitionObserver.addTaskInfosToCachedMap(taskInfo)
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
// task position changed
val newTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
@@ -591,18 +578,17 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
verify(desktopModeEventLogger, times(1))
.logTaskInfoChanged(
- eq(sessionId),
- eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1)))
+ eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))
+ )
verifyZeroInteractions(desktopModeEventLogger)
}
@Test
fun sessionAlreadyStarted_taskResized_logsTaskUpdate() {
- val sessionId = 1
// add an existing freeform task
val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM)
transitionObserver.addTaskInfosToCachedMap(taskInfo)
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
// task resized
val newTaskInfo =
@@ -618,23 +604,22 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
verify(desktopModeEventLogger, times(1))
.logTaskInfoChanged(
- eq(sessionId),
eq(
DEFAULT_TASK_UPDATE.copy(
taskWidth = DEFAULT_TASK_WIDTH + 100, taskHeight = DEFAULT_TASK_HEIGHT - 100,
- visibleTaskCount = 1)))
+ visibleTaskCount = 1))
+ )
verifyZeroInteractions(desktopModeEventLogger)
}
@Test
fun sessionAlreadyStarted_multipleTasksUpdated_logsTaskUpdateForCorrectTask() {
- val sessionId = 1
// add 2 existing freeform task
val taskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM)
val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)
transitionObserver.addTaskInfosToCachedMap(taskInfo1)
transitionObserver.addTaskInfosToCachedMap(taskInfo2)
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
// task 1 position update
val newTaskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100)
@@ -646,8 +631,9 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
verify(desktopModeEventLogger, times(1))
.logTaskInfoChanged(
- eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(
- taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2)))
+ eq(DEFAULT_TASK_UPDATE.copy(
+ taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))
+ )
verifyZeroInteractions(desktopModeEventLogger)
// task 2 resize
@@ -666,7 +652,6 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
verify(desktopModeEventLogger, times(1))
.logTaskInfoChanged(
- eq(sessionId),
eq(
DEFAULT_TASK_UPDATE.copy(
instanceId = 2,
@@ -679,11 +664,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
@Test
fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() {
- val sessionId = 1
// add two existing freeform tasks
transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM))
transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
- transitionObserver.setLoggerSessionId(sessionId)
+ transitionObserver.isSessionActive = true
// new freeform task closed
val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2))
@@ -691,9 +675,11 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
callOnTransitionReady(transitionInfo)
verify(desktopModeEventLogger, times(1))
- .logTaskRemoved(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(
- instanceId = 2, visibleTaskCount = 1)))
- verify(desktopModeEventLogger, never()).logSessionExit(any(), any())
+ .logTaskRemoved(
+ eq(DEFAULT_TASK_UPDATE.copy(
+ instanceId = 2, visibleTaskCount = 1))
+ )
+ verify(desktopModeEventLogger, never()).logSessionExit(any())
}
/** Simulate calling the onTransitionReady() method */
@@ -706,10 +692,9 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
}
private fun verifyTaskAddedAndEnterLogging(enterReason: EnterReason, taskUpdate: TaskUpdate) {
- val sessionId = transitionObserver.getLoggerSessionId()
- assertNotNull(sessionId)
- verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), eq(enterReason))
- verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), eq(taskUpdate))
+ assertTrue(transitionObserver.isSessionActive)
+ verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(enterReason))
+ verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(taskUpdate))
ExtendedMockito.verify {
Trace.setCounter(
eq(Trace.TRACE_TAG_WINDOW_MANAGER),
@@ -725,14 +710,13 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() {
}
private fun verifyTaskRemovedAndExitLogging(
- sessionId: Int,
exitReason: ExitReason,
taskUpdate: TaskUpdate
) {
- verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), eq(taskUpdate))
- verify(desktopModeEventLogger, times(1)).logSessionExit(eq(sessionId), eq(exitReason))
+ assertFalse(transitionObserver.isSessionActive)
+ verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate))
+ verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason))
verifyZeroInteractions(desktopModeEventLogger)
- assertNull(transitionObserver.getLoggerSessionId())
}
private companion object {
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 b3c10d64c3a3..f9376570dc83 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
@@ -2902,7 +2902,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
runOpenNewWindow(task)
verify(splitScreenController)
.startIntent(any(), anyInt(), any(), any(),
- optionsCaptor.capture(), anyOrNull())
+ optionsCaptor.capture(), anyOrNull(), eq(true)
+ )
assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
.isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
}
@@ -2917,7 +2918,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
verify(splitScreenController)
.startIntent(
any(), anyInt(), any(), any(),
- optionsCaptor.capture(), anyOrNull()
+ optionsCaptor.capture(), anyOrNull(), eq(true)
)
assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode)
.isEqualTo(WINDOWING_MODE_MULTI_WINDOW)
@@ -2984,6 +2985,58 @@ class DesktopTasksControllerTest : ShellTestCase() {
.launchWindowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+ fun openInstance_fromFreeform_minimizesIfNeeded() {
+ setUpLandscapeDisplay()
+ val homeTask = setUpHomeTask()
+ val freeformTasks = (1..MAX_TASK_LIMIT + 1).map { _ -> setUpFreeformTask() }
+ val oldestTask = freeformTasks.first()
+ val newestTask = freeformTasks.last()
+
+ runOpenInstance(newestTask, freeformTasks[1].taskId)
+
+ val wct = getLatestWct(type = TRANSIT_OPEN)
+ // Home is moved to front of everything.
+ assertThat(
+ wct.hierarchyOps.any { hop ->
+ hop.container == homeTask.token.asBinder() && hop.toTop
+ }
+ ).isTrue()
+ // And the oldest task isn't moved in front of home, effectively minimizing it.
+ assertThat(
+ wct.hierarchyOps.none { hop ->
+ hop.container == oldestTask.token.asBinder() && hop.toTop
+ }
+ ).isTrue()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MULTI_INSTANCE_FEATURES)
+ fun openInstance_fromFreeform_exitsImmersiveIfNeeded() {
+ setUpLandscapeDisplay()
+ val homeTask = setUpHomeTask()
+ val freeformTask = setUpFreeformTask()
+ val immersiveTask = setUpFreeformTask()
+ taskRepository.setTaskInFullImmersiveState(
+ displayId = immersiveTask.displayId,
+ taskId = immersiveTask.taskId,
+ immersive = true
+ )
+ val runOnStartTransit = RunOnStartTransitionCallback()
+ val transition = Binder()
+ whenever(transitions.startTransition(eq(TRANSIT_OPEN), any(), anyOrNull()))
+ .thenReturn(transition)
+ whenever(mockDesktopFullImmersiveTransitionHandler
+ .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId))).thenReturn(runOnStartTransit)
+
+ runOpenInstance(immersiveTask, freeformTask.taskId)
+
+ verify(mockDesktopFullImmersiveTransitionHandler)
+ .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId))
+ runOnStartTransit.assertOnlyInvocation(transition)
+ }
+
private fun runOpenInstance(
callingTask: RunningTaskInfo,
requestedTaskId: Int
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 9260a07fd945..ef3af8e7bdac 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -297,6 +297,28 @@ public class SplitScreenControllerTests extends ShellTestCase {
}
@Test
+ public void startIntent_forceLaunchNewTaskTrue_skipsBackgroundTasks() {
+ Intent startIntent = createStartIntent("startActivity");
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
+ mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
+ SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */,
+ true /* forceLaunchNewTask */);
+ verify(mRecentTasks, never()).findTaskInBackground(any(), anyInt(), any());
+ }
+
+ @Test
+ public void startIntent_forceLaunchNewTaskFalse_checksBackgroundTasks() {
+ Intent startIntent = createStartIntent("startActivity");
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
+ mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
+ SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */,
+ false /* forceLaunchNewTask */);
+ verify(mRecentTasks).findTaskInBackground(any(), anyInt(), any());
+ }
+
+ @Test
public void testSwitchSplitPosition_checksIsSplitScreenVisible() {
final String reason = "test";
when(mSplitScreenController.isSplitScreenVisible()).thenReturn(true, false);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
index cf2de91bad88..22da66dd5e67 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -18,13 +18,13 @@ package com.android.wm.shell.unfold;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
-import static android.view.WindowManager.TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH;
import static android.view.WindowManager.TRANSIT_NONE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -50,6 +50,7 @@ import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.InOrder;
import java.util.ArrayList;
import java.util.List;
@@ -140,11 +141,12 @@ public class UnfoldTransitionHandlerTest {
}
@Test
- public void startAnimation_animationHasNotFinishedYet_doesNotFinishTheTransition() {
+ public void handleFoldMergeRequest_finishesTheTransition() {
TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
-
+ // Starts the animation, the handler should wait for mShellUnfoldProgressProvider to
+ // notify about the end of the animation
mUnfoldTransitionHandler.startAnimation(
mTransition,
mock(TransitionInfo.class),
@@ -153,16 +155,24 @@ public class UnfoldTransitionHandlerTest {
finishCallback
);
- verify(finishCallback, never()).onTransitionFinished(any());
+ // Send fold transition request
+ TransitionFinishCallback mergeFinishCallback = mock(TransitionFinishCallback.class);
+ mUnfoldTransitionHandler.mergeAnimation(new Binder(), createFoldTransitionInfo(),
+ mock(SurfaceControl.Transaction.class), mTransition, mergeFinishCallback);
+
+ // Verify that fold transition is merged into unfold and that unfold is finished
+ final InOrder inOrder = inOrder(mergeFinishCallback, finishCallback);
+ inOrder.verify(mergeFinishCallback).onTransitionFinished(any());
+ inOrder.verify(finishCallback).onTransitionFinished(any());
}
@Test
- public void startAnimation_sameTransitionAsHandleRequest_startsAnimation() {
+ public void startAnimation_animationHasNotFinishedYet_doesNotFinishTheTransition() {
TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
- boolean animationStarted = mUnfoldTransitionHandler.startAnimation(
+ mUnfoldTransitionHandler.startAnimation(
mTransition,
mock(TransitionInfo.class),
mock(SurfaceControl.Transaction.class),
@@ -170,17 +180,18 @@ public class UnfoldTransitionHandlerTest {
finishCallback
);
- assertThat(animationStarted).isTrue();
+ verify(finishCallback, never()).onTransitionFinished(any());
}
@Test
- public void startAnimation_differentTransitionFromRequestWithUnfold_startsAnimation() {
- mUnfoldTransitionHandler.handleRequest(new Binder(), createNoneTransitionInfo());
+ public void startAnimation_sameTransitionAsHandleRequest_startsAnimation() {
+ TransitionRequestInfo requestInfo = createUnfoldTransitionRequestInfo();
+ mUnfoldTransitionHandler.handleRequest(mTransition, requestInfo);
TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
boolean animationStarted = mUnfoldTransitionHandler.startAnimation(
mTransition,
- createUnfoldTransitionInfo(),
+ mock(TransitionInfo.class),
mock(SurfaceControl.Transaction.class),
mock(SurfaceControl.Transaction.class),
finishCallback
@@ -196,7 +207,7 @@ public class UnfoldTransitionHandlerTest {
boolean animationStarted = mUnfoldTransitionHandler.startAnimation(
mTransition,
- createDisplayResizeTransitionInfo(),
+ createUnfoldTransitionInfo(),
mock(SurfaceControl.Transaction.class),
mock(SurfaceControl.Transaction.class),
finishCallback
@@ -247,6 +258,7 @@ public class UnfoldTransitionHandlerTest {
TransitionFinishCallback finishCallback = mock(TransitionFinishCallback.class);
mShellUnfoldProgressProvider.onStateChangeStarted();
+ mShellUnfoldProgressProvider.onStateChangeProgress(0.5f);
mShellUnfoldProgressProvider.onStateChangeFinished();
mUnfoldTransitionHandler.startAnimation(
mTransition,
@@ -279,6 +291,8 @@ public class UnfoldTransitionHandlerTest {
clearInvocations(finishCallback);
// Fold
+ mShellUnfoldProgressProvider.onStateChangeProgress(/* progress= */ 0.0f);
+ mShellUnfoldProgressProvider.onStateChangeFinished();
mShellUnfoldProgressProvider.onFoldStateChanged(/* isFolded= */ true);
// Second unfold
@@ -370,6 +384,19 @@ public class UnfoldTransitionHandlerTest {
triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */);
}
+ private TransitionInfo createFoldTransitionInfo() {
+ final TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0);
+
+ final TransitionInfo.Change change = new TransitionInfo.Change(/* container= */ null,
+ /* leash= */ null);
+ change.setFlags(TransitionInfo.FLAG_IS_DISPLAY);
+ change.setStartAbsBounds(new Rect(0, 0, 200, 200));
+ change.setEndAbsBounds(new Rect(0, 0, 100, 100));
+ transitionInfo.addChange(change);
+
+ return transitionInfo;
+ }
+
private TransitionRequestInfo createNoneTransitionInfo() {
return new TransitionRequestInfo(TRANSIT_NONE,
/* triggerTask= */ null, /* remoteTransition= */ null,
@@ -446,17 +473,6 @@ public class UnfoldTransitionHandlerTest {
change.setEndAbsBounds(new Rect(0, 0, 100, 100));
change.setFlags(TransitionInfo.FLAG_IS_DISPLAY);
transitionInfo.addChange(change);
- transitionInfo.setFlags(TRANSIT_FLAG_PHYSICAL_DISPLAY_SWITCH);
- return transitionInfo;
- }
-
- private TransitionInfo createDisplayResizeTransitionInfo() {
- TransitionInfo transitionInfo = new TransitionInfo(TRANSIT_CHANGE, /* flags= */ 0);
- TransitionInfo.Change change = new TransitionInfo.Change(null, mock(SurfaceControl.class));
- change.setStartAbsBounds(new Rect(0, 0, 10, 10));
- change.setEndAbsBounds(new Rect(0, 0, 100, 100));
- change.setFlags(TransitionInfo.FLAG_IS_DISPLAY);
- transitionInfo.addChange(change);
return transitionInfo;
}
diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt
index e9845c1d9f13..27817e9eb984 100644
--- a/libs/appfunctions/api/current.txt
+++ b/libs/appfunctions/api/current.txt
@@ -4,7 +4,6 @@ package com.google.android.appfunctions.sidecar {
public final class AppFunctionManager {
ctor public AppFunctionManager(android.content.Context);
method public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
- method @Deprecated public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
method public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>);
method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>);
field public static final int APP_FUNCTION_STATE_DEFAULT = 0; // 0x0
@@ -15,9 +14,7 @@ package com.google.android.appfunctions.sidecar {
public abstract class AppFunctionService extends android.app.Service {
ctor public AppFunctionService();
method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
- method @MainThread public void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
- method @Deprecated @MainThread public void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
- method @Deprecated @MainThread public void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
+ method @MainThread public abstract void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>);
field @NonNull public static final String BIND_APP_FUNCTION_SERVICE = "android.permission.BIND_APP_FUNCTION_SERVICE";
field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
}
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
index d660926575d1..43377d8eb91c 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java
@@ -126,33 +126,6 @@ public final class AppFunctionManager {
}
/**
- * Executes the app function.
- *
- * <p>Proxies request and response to the underlying {@link
- * android.app.appfunctions.AppFunctionManager#executeAppFunction}, converting the request and
- * response in the appropriate type required by the function.
- *
- * @deprecated Use {@link #executeAppFunction(ExecuteAppFunctionRequest, Executor,
- * CancellationSignal, Consumer)} instead. This method will be removed once usage references
- * are updated.
- */
- @Deprecated
- public void executeAppFunction(
- @NonNull ExecuteAppFunctionRequest sidecarRequest,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
- Objects.requireNonNull(sidecarRequest);
- Objects.requireNonNull(executor);
- Objects.requireNonNull(callback);
-
- executeAppFunction(
- sidecarRequest,
- executor,
- new CancellationSignal(),
- callback);
- }
-
- /**
* Returns a boolean through a callback, indicating whether the app function is enabled.
*
* <p>* This method can only check app functions owned by the caller, or those where the caller
diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
index 2a168e871713..0dc87e45b7e3 100644
--- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
+++ b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java
@@ -119,76 +119,9 @@ public abstract class AppFunctionService extends Service {
* @param callback A callback to report back the result.
*/
@MainThread
- public void onExecuteFunction(
+ public abstract void onExecuteFunction(
@NonNull ExecuteAppFunctionRequest request,
@NonNull String callingPackage,
@NonNull CancellationSignal cancellationSignal,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
- onExecuteFunction(request, cancellationSignal, callback);
- }
-
- /**
- * Called by the system to execute a specific app function.
- *
- * <p>This method is triggered when the system requests your AppFunctionService to handle a
- * particular function you have registered and made available.
- *
- * <p>To ensure proper routing of function requests, assign a unique identifier to each
- * function. This identifier doesn't need to be globally unique, but it must be unique within
- * your app. For example, a function to order food could be identified as "orderFood". In most
- * cases this identifier should come from the ID automatically generated by the AppFunctions
- * SDK. You can determine the specific function to invoke by calling {@link
- * ExecuteAppFunctionRequest#getFunctionIdentifier()}.
- *
- * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker
- * thread and dispatch the result with the given callback. You should always report back the
- * result using the callback, no matter if the execution was successful or not.
- *
- * @param request The function execution request.
- * @param cancellationSignal A {@link CancellationSignal} to cancel the request.
- * @param callback A callback to report back the result.
- * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, String,
- * CancellationSignal, Consumer)} instead. This method will be removed once usage references
- * are updated.
- */
- @MainThread
- @Deprecated
- public void onExecuteFunction(
- @NonNull ExecuteAppFunctionRequest request,
- @NonNull CancellationSignal cancellationSignal,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
- onExecuteFunction(request, callback);
- }
-
- /**
- * Called by the system to execute a specific app function.
- *
- * <p>This method is triggered when the system requests your AppFunctionService to handle a
- * particular function you have registered and made available.
- *
- * <p>To ensure proper routing of function requests, assign a unique identifier to each
- * function. This identifier doesn't need to be globally unique, but it must be unique within
- * your app. For example, a function to order food could be identified as "orderFood". In most
- * cases this identifier should come from the ID automatically generated by the AppFunctions
- * SDK. You can determine the specific function to invoke by calling {@link
- * ExecuteAppFunctionRequest#getFunctionIdentifier()}.
- *
- * <p>This method is always triggered in the main thread. You should run heavy tasks on a worker
- * thread and dispatch the result with the given callback. You should always report back the
- * result using the callback, no matter if the execution was successful or not.
- *
- * @param request The function execution request.
- * @param callback A callback to report back the result.
- * @deprecated Use {@link #onExecuteFunction(ExecuteAppFunctionRequest, CancellationSignal,
- * Consumer)} instead. This method will be removed once usage references are updated.
- */
- @MainThread
- @Deprecated
- public void onExecuteFunction(
- @NonNull ExecuteAppFunctionRequest request,
- @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
- Log.w(
- "AppFunctionService",
- "Calling deprecated default implementation of onExecuteFunction");
- }
+ @NonNull Consumer<ExecuteAppFunctionResponse> callback);
}
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 28d85bd860df..a3737131702d 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -242,45 +242,59 @@ void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
}
}
-SkRect DamageAccumulator::computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const {
- const DirtyStack* frame = mHead;
- Matrix4 transform;
- SkRect pretransformResult = bounds;
- while (true) {
- SkRect currentBounds = pretransformResult;
- pretransformResult.setEmpty();
- switch (frame->type) {
- case TransformRenderNode: {
- const RenderProperties& props = frame->renderNode->properties();
- // Perform clipping
- if (props.getClipDamageToBounds() && !currentBounds.isEmpty()) {
- if (!currentBounds.intersect(
- SkRect::MakeIWH(props.getWidth(), props.getHeight()))) {
- currentBounds.setEmpty();
- }
+static void computeClipAndTransformImpl(const DirtyStack* currentFrame, SkRect* crop,
+ Matrix4* outMatrix) {
+ SkRect currentCrop = *crop;
+ switch (currentFrame->type) {
+ case TransformRenderNode: {
+ const RenderProperties& props = currentFrame->renderNode->properties();
+ // Perform clipping
+ if (props.getClipDamageToBounds() && !currentCrop.isEmpty()) {
+ if (!currentCrop.intersect(SkRect::MakeIWH(props.getWidth(), props.getHeight()))) {
+ currentCrop.setEmpty();
}
+ }
- // apply all transforms
- mapRect(props, currentBounds, &pretransformResult);
- frame->renderNode->applyViewPropertyTransforms(transform);
- } break;
- case TransformMatrix4:
- mapRect(frame->matrix4, currentBounds, &pretransformResult);
- transform.multiply(*frame->matrix4);
- break;
- default:
- pretransformResult = currentBounds;
- break;
- }
- if (frame->prev == frame) break;
- frame = frame->prev;
+ // apply all transforms
+ crop->setEmpty();
+ mapRect(props, currentCrop, crop);
+ } break;
+ case TransformMatrix4:
+ crop->setEmpty();
+ mapRect(currentFrame->matrix4, currentCrop, crop);
+ break;
+ default:
+ break;
+ }
+
+ if (currentFrame->prev != currentFrame) {
+ computeClipAndTransformImpl(currentFrame->prev, crop, outMatrix);
+ }
+ switch (currentFrame->type) {
+ case TransformRenderNode:
+ currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix);
+ break;
+ case TransformMatrix4:
+ outMatrix->multiply(*currentFrame->matrix4);
+ break;
+ case TransformNone:
+ // nothing to be done
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d",
+ currentFrame->type);
}
- SkRect result;
+}
+
+SkRect DamageAccumulator::computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const {
+ SkRect cropInGlobal = bounds;
+ outMatrix->loadIdentity();
+ computeClipAndTransformImpl(mHead, &cropInGlobal, outMatrix);
+ SkRect cropInLocal;
Matrix4 globalToLocal;
- globalToLocal.loadInverse(transform);
- mapRect(&globalToLocal, pretransformResult, &result);
- *outMatrix = transform;
- return result;
+ globalToLocal.loadInverse(*outMatrix);
+ mapRect(&globalToLocal, cropInGlobal, &cropInLocal);
+ return cropInLocal;
}
void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 84bd45dfc012..b73380e38fd1 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -582,20 +582,22 @@ size_t Bitmap::mTotalBitmapBytes = 0;
size_t Bitmap::mTotalBitmapCount = 0;
void Bitmap::traceBitmapCreate() {
+ size_t bytes = getAllocationByteCount();
+ std::lock_guard lock{mLock};
+ mTotalBitmapBytes += bytes;
+ mTotalBitmapCount++;
if (ATRACE_ENABLED()) {
- std::lock_guard lock{mLock};
- mTotalBitmapBytes += getAllocationByteCount();
- mTotalBitmapCount++;
ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes);
ATRACE_INT64("Bitmap Count", mTotalBitmapCount);
}
}
void Bitmap::traceBitmapDelete() {
+ size_t bytes = getAllocationByteCount();
+ std::lock_guard lock{mLock};
+ mTotalBitmapBytes -= getAllocationByteCount();
+ mTotalBitmapCount--;
if (ATRACE_ENABLED()) {
- std::lock_guard lock{mLock};
- mTotalBitmapBytes -= getAllocationByteCount();
- mTotalBitmapCount--;
ATRACE_INT64("Bitmap Memory", mTotalBitmapBytes);
ATRACE_INT64("Bitmap Count", mTotalBitmapCount);
}
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index 456338631ae4..70e6beda6cb9 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -31,12 +31,34 @@
namespace android {
+struct FakedFontKey {
+ uint32_t operator()(const minikin::FakedFont& fakedFont) const {
+ return minikin::Hasher()
+ .update(reinterpret_cast<uintptr_t>(fakedFont.font.get()))
+ .update(fakedFont.fakery.bits())
+ .update(fakedFont.fakery.variationSettings())
+ .hash();
+ }
+};
+
struct LayoutWrapper {
LayoutWrapper(minikin::Layout&& layout, float ascent, float descent)
: layout(std::move(layout)), ascent(ascent), descent(descent) {}
+
+ LayoutWrapper(minikin::Layout&& layout, float ascent, float descent, std::vector<jlong>&& fonts,
+ std::vector<uint32_t>&& fontIds)
+ : layout(std::move(layout))
+ , ascent(ascent)
+ , descent(descent)
+ , fonts(std::move(fonts))
+ , fontIds(std::move(fontIds)) {}
+
minikin::Layout layout;
float ascent;
float descent;
+
+ std::vector<jlong> fonts;
+ std::vector<uint32_t> fontIds; // per glyph
};
static void releaseLayout(jlong ptr) {
@@ -64,6 +86,43 @@ static jlong shapeTextRun(const uint16_t* text, int textSize, int start, int cou
overallDescent = std::max(overallDescent, extent.descent);
}
+ if (text_feature::typeface_redesign()) {
+ uint32_t runCount = layout.getFontRunCount();
+
+ std::unordered_map<minikin::FakedFont, uint32_t, FakedFontKey> fakedToFontIds;
+ std::vector<jlong> fonts;
+ std::vector<uint32_t> fontIds;
+
+ fontIds.resize(layout.nGlyphs());
+ for (uint32_t ri = 0; ri < runCount; ++ri) {
+ const minikin::FakedFont& fakedFont = layout.getFontRunFont(ri);
+
+ auto it = fakedToFontIds.find(fakedFont);
+ uint32_t fontId;
+ if (it != fakedToFontIds.end()) {
+ fontId = it->second; // We've seen it.
+ } else {
+ fontId = fonts.size(); // This is new to us. Create new one.
+ std::shared_ptr<minikin::Font> font = std::make_shared<minikin::Font>(
+ fakedFont.font, fakedFont.fakery.variationSettings());
+ fonts.push_back(reinterpret_cast<jlong>(new FontWrapper(std::move(font))));
+ fakedToFontIds.insert(std::make_pair(fakedFont, fontId));
+ }
+
+ const uint32_t runStart = layout.getFontRunStart(ri);
+ const uint32_t runEnd = layout.getFontRunEnd(ri);
+ for (uint32_t i = runStart; i < runEnd; ++i) {
+ fontIds[i] = fontId;
+ }
+ }
+
+ std::unique_ptr<LayoutWrapper> ptr =
+ std::make_unique<LayoutWrapper>(std::move(layout), overallAscent, overallDescent,
+ std::move(fonts), std::move(fontIds));
+
+ return reinterpret_cast<jlong>(ptr.release());
+ }
+
std::unique_ptr<LayoutWrapper> ptr = std::make_unique<LayoutWrapper>(
std::move(layout), overallAscent, overallDescent
);
@@ -156,6 +215,8 @@ static jboolean TextShaper_Result_getFakeItalic(CRITICAL_JNI_PARAMS_COMMA jlong
return layout->layout.getFakery(i).isFakeItalic();
}
+constexpr float NO_OVERRIDE = -1;
+
float findValueFromVariationSettings(const minikin::FontFakery& fakery, minikin::AxisTag tag) {
for (const minikin::FontVariation& fv : fakery.variationSettings()) {
if (fv.axisTag == tag) {
@@ -171,12 +232,7 @@ static jfloat TextShaper_Result_getWeightOverride(CRITICAL_JNI_PARAMS_COMMA jlon
if (text_feature::typeface_redesign()) {
float value =
findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_wght);
- if (!std::isnan(value)) {
- return value;
- } else {
- const std::shared_ptr<minikin::Font>& font = layout->layout.getFontRef(i);
- return font->style().weight();
- }
+ return std::isnan(value) ? NO_OVERRIDE : value;
} else {
return layout->layout.getFakery(i).wghtAdjustment();
}
@@ -188,12 +244,7 @@ static jfloat TextShaper_Result_getItalicOverride(CRITICAL_JNI_PARAMS_COMMA jlon
if (text_feature::typeface_redesign()) {
float value =
findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_ital);
- if (!std::isnan(value)) {
- return value;
- } else {
- const std::shared_ptr<minikin::Font>& font = layout->layout.getFontRef(i);
- return font->style().isItalic();
- }
+ return std::isnan(value) ? NO_OVERRIDE : value;
} else {
return layout->layout.getFakery(i).italAdjustment();
}
@@ -207,6 +258,24 @@ static jlong TextShaper_Result_getFont(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint
}
// CriticalNative
+static jint TextShaper_Result_getFontCount(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->fonts.size();
+}
+
+// CriticalNative
+static jlong TextShaper_Result_getFontRef(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint fontId) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->fonts[fontId];
+}
+
+// CriticalNative
+static jint TextShaper_Result_getFontId(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint glyphIdx) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->fontIds[glyphIdx];
+}
+
+// CriticalNative
static jlong TextShaper_Result_nReleaseFunc(CRITICAL_JNI_PARAMS) {
return reinterpret_cast<jlong>(releaseLayout);
}
@@ -250,6 +319,10 @@ static const JNINativeMethod gResultMethods[] = {
{"nGetWeightOverride", "(JI)F", (void*)TextShaper_Result_getWeightOverride},
{"nGetItalicOverride", "(JI)F", (void*)TextShaper_Result_getItalicOverride},
{"nReleaseFunc", "()J", (void*)TextShaper_Result_nReleaseFunc},
+
+ {"nGetFontCount", "(J)I", (void*)TextShaper_Result_getFontCount},
+ {"nGetFontRef", "(JI)J", (void*)TextShaper_Result_getFontRef},
+ {"nGetFontId", "(JI)I", (void*)TextShaper_Result_getFontId},
};
int register_android_graphics_text_TextShaper(JNIEnv* env) {
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index eeb4853afadc..961962f6a010 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -85,8 +85,7 @@ interface IMediaRouterService {
void updateScanningState(IMediaRouter2Manager manager, @JavaPassthrough(annotation="@android.media.MediaRouter2.ScanningState") int scanningState);
void requestCreateSessionWithManager(IMediaRouter2Manager manager, int requestId,
- in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route,
- in UserHandle transferInitiatorUserHandle, in String transferInitiatorPackageName);
+ in RoutingSessionInfo oldSession, in @nullable MediaRoute2Info route);
void selectRouteWithManager(IMediaRouter2Manager manager, int requestId,
String sessionId, in MediaRoute2Info route);
void deselectRouteWithManager(IMediaRouter2Manager manager, int requestId,
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index b84990b54bd5..3499c438086d 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -2770,7 +2770,7 @@ public final class MediaRouter2 {
|| isSystemRouteReselection) {
transferToRoute(sessionInfo, route, mClientUser, mClientPackageName);
} else {
- requestCreateSession(sessionInfo, route, mClientUser, mClientPackageName);
+ requestCreateSession(sessionInfo, route);
}
}
@@ -2826,10 +2826,7 @@ public final class MediaRouter2 {
* @param route The {@link MediaRoute2Info route} to transfer to.
*/
private void requestCreateSession(
- @NonNull RoutingSessionInfo oldSession,
- @NonNull MediaRoute2Info route,
- @NonNull UserHandle transferInitiatorUserHandle,
- @NonNull String transferInitiatorPackageName) {
+ @NonNull RoutingSessionInfo oldSession, @NonNull MediaRoute2Info route) {
if (TextUtils.isEmpty(oldSession.getClientPackageName())) {
Log.w(TAG, "requestCreateSession: Can't create a session without package name.");
this.onTransferFailed(oldSession, route);
@@ -2840,12 +2837,7 @@ public final class MediaRouter2 {
try {
mMediaRouterService.requestCreateSessionWithManager(
- mClient,
- requestId,
- oldSession,
- route,
- transferInitiatorUserHandle,
- transferInitiatorPackageName);
+ mClient, requestId, oldSession, route);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 8fa0e49e8b96..7e1dccf2d366 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -524,8 +524,7 @@ public final class MediaRouter2Manager {
transferToRoute(
sessionInfo, route, transferInitiatorUserHandle, transferInitiatorPackageName);
} else {
- requestCreateSession(sessionInfo, route, transferInitiatorUserHandle,
- transferInitiatorPackageName);
+ requestCreateSession(sessionInfo, route);
}
}
@@ -914,9 +913,7 @@ public final class MediaRouter2Manager {
}
}
- private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route,
- @NonNull UserHandle transferInitiatorUserHandle,
- @NonNull String transferInitiationPackageName) {
+ private void requestCreateSession(RoutingSessionInfo oldSession, MediaRoute2Info route) {
if (TextUtils.isEmpty(oldSession.getClientPackageName())) {
Log.w(TAG, "requestCreateSession: Can't create a session without package name.");
notifyTransferFailed(oldSession, route);
@@ -927,8 +924,7 @@ public final class MediaRouter2Manager {
try {
mMediaRouterService.requestCreateSessionWithManager(
- mClient, requestId, oldSession, route, transferInitiatorUserHandle,
- transferInitiationPackageName);
+ mClient, requestId, oldSession, route);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
index b2861826a103..601e001f48c2 100644
--- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -57,7 +57,7 @@ import java.util.List;
* 1. User sets invisible for button. ex: ActionButtonPreference.setButton1Visible(false)
* 2. User doesn't set any title or icon for button.
*/
-public class ActionButtonsPreference extends Preference {
+public class ActionButtonsPreference extends Preference implements GroupSectionDividerMixin {
private static final String TAG = "ActionButtonPreference";
private static final boolean mIsAtLeastS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
index 6cd777e878fe..10769ecfbe42 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
@@ -43,7 +43,7 @@ import com.android.settingslib.widget.preference.banner.R;
* Banner message is a banner displaying important information (permission request, page error etc),
* and provide actions for user to address. It requires a user action to be dismissed.
*/
-public class BannerMessagePreference extends Preference {
+public class BannerMessagePreference extends Preference implements GroupSectionDividerMixin {
public enum AttentionLevel {
HIGH(0, R.color.banner_background_attention_high, R.color.banner_accent_attention_high),
diff --git a/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt
index eb14746a0f22..84ff1bbef65d 100644
--- a/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt
+++ b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt
@@ -30,7 +30,7 @@ class CardPreference @JvmOverloads constructor(
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
-) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin {
init {
layoutResource = R.layout.settingslib_expressive_preference_card
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/non_collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/non_collapsing_toolbar_content_layout.xml
index 33519cba2940..dd7eac776583 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/non_collapsing_toolbar_content_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/non_collapsing_toolbar_content_layout.xml
@@ -26,12 +26,12 @@
android:outlineAmbientShadowColor="@android:color/transparent"
android:outlineSpotShadowColor="@android:color/transparent"
android:background="@android:color/transparent"
- android:theme="@style/Theme.CollapsingToolbar.Settings">
+ android:theme="@style/ThemeOverlay.MaterialComponents.PlatformBridge.CollapsingToolbar">
<Toolbar
android:id="@+id/action_bar"
android:layout_width="match_parent"
- android:layout_height="?attr/actionBarSize"
+ android:layout_height="?android:attr/actionBarSize"
android:theme="?android:attr/actionBarTheme"
android:transitionName="shared_element_view"
app:layout_collapseMode="pin"/>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml
index c20beaf9bf93..02f171cf0d9e 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml
@@ -21,4 +21,15 @@
<item name="colorPrimary">@color/settingslib_primary_dark_device_default_settings</item>
<item name="colorAccent">@color/settingslib_accent_device_default_dark</item>
</style>
-</resources> \ No newline at end of file
+
+ <!--
+ ~ TODO(b/349675008): Remove this theme overlay once the platform bridge theme properly sets
+ ~ the MaterialComponents colors based on the platform theme.
+ -->
+ <style name="ThemeOverlay.MaterialComponents.PlatformBridge.CollapsingToolbar">
+ <item name="elevationOverlayEnabled">true</item>
+ <item name="elevationOverlayColor">?attr/colorPrimary</item>
+ <item name="colorPrimary">@color/settingslib_primary_dark_device_default_settings</item>
+ <item name="colorAccent">@color/settingslib_accent_device_default_dark</item>
+ </style>
+</resources>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml
index 9ecc297c6d36..403931764d7e 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml
@@ -21,4 +21,15 @@
<item name="colorPrimary">@color/settingslib_primary_device_default_settings_light</item>
<item name="colorAccent">@color/settingslib_accent_device_default_light</item>
</style>
-</resources> \ No newline at end of file
+
+ <!--
+ ~ TODO(b/349675008): Remove this theme overlay once the platform bridge theme properly sets
+ ~ the MaterialComponents colors based on the platform theme.
+ -->
+ <style name="ThemeOverlay.MaterialComponents.PlatformBridge.CollapsingToolbar">
+ <item name="elevationOverlayEnabled">true</item>
+ <item name="elevationOverlayColor">?attr/colorPrimary</item>
+ <item name="colorPrimary">@color/settingslib_primary_device_default_settings_light</item>
+ <item name="colorAccent">@color/settingslib_accent_device_default_light</item>
+ </style>
+</resources>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes_bridge.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes_bridge.xml
new file mode 100644
index 000000000000..bcb9baf94706
--- /dev/null
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes_bridge.xml
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ 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.
+-->
+
+<!-- See appcompat/appcompat/THEMES for the theme structure. -->
+<resources>
+ <!--
+ ~ Bridge theme overlay to simulate AppCompat themes based on a platform theme.
+ ~ Only non-widget attributes are included here since we should still use the platform widgets.
+ ~ Only public theme attributes (as in platform public-final.xml) can be referenced here since
+ ~ this is used in modules.
+ -->
+ <style name="Base.V31.ThemeOverlay.AppCompat.PlatformBridge" parent="">
+ <!-- START Base.V7.Theme.AppCompat -->
+
+ <item name="colorBackgroundFloating">?android:colorBackgroundFloating</item>
+
+ <item name="isLightTheme">?android:isLightTheme</item>
+
+ <item name="selectableItemBackground">?android:selectableItemBackground</item>
+ <item name="selectableItemBackgroundBorderless">?android:selectableItemBackgroundBorderless</item>
+ <item name="homeAsUpIndicator">?android:homeAsUpIndicator</item>
+
+ <item name="dividerVertical">?android:dividerVertical</item>
+ <item name="dividerHorizontal">?android:dividerHorizontal</item>
+
+ <!-- List attributes -->
+ <item name="textAppearanceListItem">?android:textAppearanceListItem</item>
+ <item name="textAppearanceListItemSmall">?android:textAppearanceListItemSmall</item>
+ <item name="textAppearanceListItemSecondary">?android:textAppearanceListItemSecondary</item>
+ <item name="listPreferredItemHeight">?android:listPreferredItemHeight</item>
+ <item name="listPreferredItemHeightSmall">?android:listPreferredItemHeightSmall</item>
+ <item name="listPreferredItemHeightLarge">?android:listPreferredItemHeightLarge</item>
+ <item name="listPreferredItemPaddingLeft">?android:listPreferredItemPaddingLeft</item>
+ <item name="listPreferredItemPaddingRight">?android:listPreferredItemPaddingRight</item>
+ <item name="listPreferredItemPaddingStart">?android:listPreferredItemPaddingStart</item>
+ <item name="listPreferredItemPaddingEnd">?android:listPreferredItemPaddingEnd</item>
+
+ <!-- Color palette -->
+ <item name="colorPrimaryDark">?android:colorPrimaryDark</item>
+ <item name="colorPrimary">?android:colorPrimary</item>
+ <item name="colorAccent">?android:colorAccent</item>
+
+ <item name="colorControlNormal">?android:colorControlNormal</item>
+ <item name="colorControlActivated">?android:colorControlActivated</item>
+ <item name="colorControlHighlight">?android:colorControlHighlight</item>
+ <item name="colorButtonNormal">?android:colorButtonNormal</item>
+
+ <item name="colorError">?android:colorError</item>
+
+ <!-- END Base.V7.Theme.AppCompat -->
+ </style>
+ <style name="Base.ThemeOverlay.AppCompat.PlatformBridge" parent="Base.V31.ThemeOverlay.AppCompat.PlatformBridge" />
+ <style name="ThemeOverlay.AppCompat.PlatformBridge" parent="Base.ThemeOverlay.AppCompat.PlatformBridge" />
+
+ <!--
+ ~ Bridge theme overlay to simulate MaterialComponents themes based on a platform theme.
+ -->
+ <style name="Base.V31.ThemeOverlay.MaterialComponents.PlatformBridge" parent="ThemeOverlay.AppCompat.PlatformBridge">
+ <!-- START Base.V14.Theme.MaterialComponents.Bridge -->
+ <!--
+ ~ This is copied as-is from the original bridge theme since it is guaranteed to not affect
+ ~ existing widgets.
+ -->
+
+ <item name="isMaterialTheme">true</item>
+
+ <item name="colorPrimaryVariant">@color/design_dark_default_color_primary_variant</item>
+ <item name="colorSecondary">@color/design_dark_default_color_secondary</item>
+ <item name="colorSecondaryVariant">@color/design_dark_default_color_secondary_variant</item>
+ <item name="colorSurface">@color/design_dark_default_color_surface</item>
+ <item name="colorPrimarySurface">?attr/colorSurface</item>
+ <item name="colorOnPrimary">@color/design_dark_default_color_on_primary</item>
+ <item name="colorOnSecondary">@color/design_dark_default_color_on_secondary</item>
+ <item name="colorOnBackground">@color/design_dark_default_color_on_background</item>
+ <item name="colorOnError">@color/design_dark_default_color_on_error</item>
+ <item name="colorOnSurface">@color/design_dark_default_color_on_surface</item>
+ <item name="colorOnPrimarySurface">?attr/colorOnSurface</item>
+
+ <item name="scrimBackground">@color/mtrl_scrim_color</item>
+ <item name="popupMenuBackground">@drawable/mtrl_popupmenu_background_overlay</item>
+
+ <item name="minTouchTargetSize">@dimen/mtrl_min_touch_target_size</item>
+
+ <!-- MaterialComponents Widget styles -->
+ <item name="badgeStyle">@style/Widget.MaterialComponents.Badge</item>
+ <item name="bottomAppBarStyle">@style/Widget.MaterialComponents.BottomAppBar</item>
+ <item name="chipStyle">@style/Widget.MaterialComponents.Chip.Action</item>
+ <item name="chipGroupStyle">@style/Widget.MaterialComponents.ChipGroup</item>
+ <item name="chipStandaloneStyle">@style/Widget.MaterialComponents.Chip.Entry</item>
+ <item name="circularProgressIndicatorStyle">@style/Widget.MaterialComponents.CircularProgressIndicator</item>
+ <item name="extendedFloatingActionButtonStyle">@style/Widget.MaterialComponents.ExtendedFloatingActionButton.Icon</item>
+ <item name="linearProgressIndicatorStyle">@style/Widget.MaterialComponents.LinearProgressIndicator</item>
+ <item name="materialButtonStyle">@style/Widget.MaterialComponents.Button</item>
+ <item name="materialButtonOutlinedStyle">@style/Widget.MaterialComponents.Button.OutlinedButton</item>
+ <item name="materialButtonToggleGroupStyle">@style/Widget.MaterialComponents.MaterialButtonToggleGroup</item>
+ <item name="materialCardViewStyle">@style/Widget.MaterialComponents.CardView</item>
+ <item name="navigationRailStyle">@style/Widget.MaterialComponents.NavigationRailView</item>
+ <item name="sliderStyle">@style/Widget.MaterialComponents.Slider</item>
+
+ <!-- Type styles -->
+ <item name="textAppearanceHeadline1">@style/TextAppearance.MaterialComponents.Headline1</item>
+ <item name="textAppearanceHeadline2">@style/TextAppearance.MaterialComponents.Headline2</item>
+ <item name="textAppearanceHeadline3">@style/TextAppearance.MaterialComponents.Headline3</item>
+ <item name="textAppearanceHeadline4">@style/TextAppearance.MaterialComponents.Headline4</item>
+ <item name="textAppearanceHeadline5">@style/TextAppearance.MaterialComponents.Headline5</item>
+ <item name="textAppearanceHeadline6">@style/TextAppearance.MaterialComponents.Headline6</item>
+ <item name="textAppearanceSubtitle1">@style/TextAppearance.MaterialComponents.Subtitle1</item>
+ <item name="textAppearanceSubtitle2">@style/TextAppearance.MaterialComponents.Subtitle2</item>
+ <item name="textAppearanceBody1">@style/TextAppearance.MaterialComponents.Body1</item>
+ <item name="textAppearanceBody2">@style/TextAppearance.MaterialComponents.Body2</item>
+ <item name="textAppearanceCaption">@style/TextAppearance.MaterialComponents.Caption</item>
+ <item name="textAppearanceButton">@style/TextAppearance.MaterialComponents.Button</item>
+ <item name="textAppearanceOverline">@style/TextAppearance.MaterialComponents.Overline</item>
+
+ <!-- Shape styles -->
+ <item name="shapeAppearanceSmallComponent">
+ @style/ShapeAppearance.MaterialComponents.SmallComponent
+ </item>
+ <item name="shapeAppearanceMediumComponent">
+ @style/ShapeAppearance.MaterialComponents.MediumComponent
+ </item>
+ <item name="shapeAppearanceLargeComponent">
+ @style/ShapeAppearance.MaterialComponents.LargeComponent
+ </item>
+
+ <!-- Motion -->
+ <item name="motionEasingStandard">@string/material_motion_easing_standard</item>
+ <item name="motionEasingEmphasized">@string/material_motion_easing_emphasized</item>
+ <item name="motionEasingDecelerated">@string/material_motion_easing_decelerated</item>
+ <item name="motionEasingAccelerated">@string/material_motion_easing_accelerated</item>
+ <item name="motionEasingLinear">@string/material_motion_easing_linear</item>
+
+ <item name="motionDurationShort1">@integer/material_motion_duration_short_1</item>
+ <item name="motionDurationShort2">@integer/material_motion_duration_short_2</item>
+ <item name="motionDurationMedium1">@integer/material_motion_duration_medium_1</item>
+ <item name="motionDurationMedium2">@integer/material_motion_duration_medium_2</item>
+ <item name="motionDurationLong1">@integer/material_motion_duration_long_1</item>
+ <item name="motionDurationLong2">@integer/material_motion_duration_long_2</item>
+
+ <item name="motionPath">@integer/material_motion_path</item>
+
+ <!-- Elevation Overlays -->
+ <item name="elevationOverlayEnabled">true</item>
+ <item name="elevationOverlayColor">?attr/colorOnSurface</item>
+
+ <!-- END Base.V14.Theme.MaterialComponents.Bridge -->
+
+ <!-- START Base.V14.Theme.MaterialComponents -->
+ <!--
+ ~ Only a subset of widget attributes being actually used are included here since there are
+ ~ too many of them and they need to be investigated on a case-by-case basis.
+ -->
+
+ <!-- Framework, AppCompat, or Design Widget styles -->
+ <item name="appBarLayoutStyle">@style/Widget.MaterialComponents.AppBarLayout.Surface</item>
+
+ <!-- END Base.V14.Theme.MaterialComponents -->
+ </style>
+ <style name="Base.ThemeOverlay.MaterialComponents.PlatformBridge" parent="Base.V31.ThemeOverlay.AppCompat.PlatformBridge" />
+ <style name="ThemeOverlay.MaterialComponents.PlatformBridge" parent="Base.ThemeOverlay.AppCompat.PlatformBridge" />
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
index cd8f584953aa..12890feaf56e 100644
--- a/packages/SettingsLib/IllustrationPreference/Android.bp
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -21,6 +21,7 @@ android_library {
"SettingsLibColor",
"androidx.preference_preference",
"lottie",
+ "SettingsLibSettingsTheme",
"settingslib_illustrationpreference_flags_lib",
],
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index a0599bb32dd1..adc4f316aca9 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -55,7 +55,7 @@ import java.io.InputStream;
/**
* IllustrationPreference is a preference that can play lottie format animation
*/
-public class IllustrationPreference extends Preference {
+public class IllustrationPreference extends Preference implements GroupSectionDividerMixin {
private static final String TAG = "IllustrationPreference";
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
index e3f8fbb88a65..2e3ee32e2efb 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
@@ -20,8 +20,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?android:attr/listPreferredItemHeight"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingTop="@dimen/settingslib_switchbar_margin"
android:paddingBottom="@dimen/settingslib_switchbar_margin"
android:orientation="vertical">
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
index 255b2c92e709..3e0e18488f36 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
@@ -20,8 +20,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?android:attr/listPreferredItemHeight"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingTop="@dimen/settingslib_switchbar_margin"
android:paddingBottom="@dimen/settingslib_switchbar_margin"
android:orientation="vertical">
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml
index 4425ef08d6e3..f75d9b23c49b 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml
@@ -20,8 +20,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?android:attr/listPreferredItemHeight"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingVertical="@dimen/settingslib_expressive_space_small1"
android:orientation="vertical">
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
index bf34db93298b..7c0eaeaca3de 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
@@ -18,11 +18,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingRight="?android:attr/listPreferredItemPaddingRight"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:layout_width="match_parent">
<TextView
android:id="@+id/switch_text"
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml
index bef6e352d854..fa908a4ed6c8 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml
@@ -18,6 +18,10 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
+ android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:importantForAccessibility="no">
<com.android.settingslib.widget.MainSwitchBar
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
index d895c874d95e..3394874797e3 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java
@@ -36,7 +36,8 @@ import java.util.List;
* This component is used as the main switch of the page
* to enable or disable the prefereces on the page.
*/
-public class MainSwitchPreference extends TwoStatePreference implements OnCheckedChangeListener {
+public class MainSwitchPreference extends TwoStatePreference
+ implements OnCheckedChangeListener, GroupSectionDividerMixin {
private final List<OnCheckedChangeListener> mSwitchChangeListeners = new ArrayList<>();
diff --git a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt
index e27838c4ef4d..89881f4d74bb 100644
--- a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt
+++ b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt
@@ -27,6 +27,7 @@ import androidx.preference.PreferenceScreen
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.atomic.AtomicBoolean
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,7 +37,7 @@ import org.junit.runner.RunWith
abstract class CatalystScreenTestCase {
@get:Rule val setFlagsRule = SetFlagsRule()
- protected val context: Context = ApplicationProvider.getApplicationContext()
+ protected val appContext: Context = ApplicationProvider.getApplicationContext()
/** Catalyst screen. */
protected abstract val preferenceScreenCreator: PreferenceScreenCreator
@@ -51,12 +52,12 @@ abstract class CatalystScreenTestCase {
@Test
open fun migration() {
enableCatalystScreen()
- assertThat(preferenceScreenCreator.isFlagEnabled(context)).isTrue()
+ assertThat(preferenceScreenCreator.isFlagEnabled(appContext)).isTrue()
val catalystScreen = dumpPreferenceScreen()
- Log.i("Catalyst", catalystScreen)
+ Log.i(TAG, catalystScreen)
disableCatalystScreen()
- assertThat(preferenceScreenCreator.isFlagEnabled(context)).isFalse()
+ assertThat(preferenceScreenCreator.isFlagEnabled(appContext)).isFalse()
val legacyScreen = dumpPreferenceScreen()
assertThat(catalystScreen).isEqualTo(legacyScreen)
@@ -83,11 +84,26 @@ abstract class CatalystScreenTestCase {
}
private fun dumpPreferenceScreen(): String {
+ // Dump threads for troubleshooting when the test thread is stuck.
+ // Latest junit Timeout rule supports similar feature but it is not yet available on AOSP.
+ val taskFinished = AtomicBoolean()
+ Thread {
+ Thread.sleep(20000)
+ if (!taskFinished.get()) dumpThreads()
+ }
+ .apply {
+ isDaemon = true
+ start()
+ }
+
@Suppress("UNCHECKED_CAST")
val clazz = preferenceScreenCreator.fragmentClass() as Class<PreferenceFragmentCompat>
val builder = StringBuilder()
FragmentScenario.launch(clazz).use {
- it.onFragment { fragment -> fragment.preferenceScreen.toString(builder) }
+ it.onFragment { fragment ->
+ taskFinished.set(true)
+ fragment.preferenceScreen.toString(builder)
+ }
}
return builder.toString()
}
@@ -120,4 +136,16 @@ abstract class CatalystScreenTestCase {
}
builder.append(indent).append("}\n")
}
+
+ companion object {
+ const val TAG = "CatalystScreenTestCase"
+
+ fun dumpThreads() {
+ for ((thread, stack) in Thread.getAllStackTraces()) {
+ Log.i(TAG, "$thread")
+ for (frame in stack) Log.i(TAG, " $frame")
+ Log.i(TAG, "")
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt
new file mode 100644
index 000000000000..ba5f5cf855be
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.settingslib.widget
+
+/**
+ * A base interface to indicate that a class should not have rounded corners.
+ *
+ * Classes implementing this interface will be treated as already handle rounded corners.
+ */
+interface GroupSectionDividerMixin \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
new file mode 100644
index 000000000000..535d80f609fb
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.settingslib.widget
+
+import androidx.preference.PreferenceFragmentCompat
+import androidx.preference.PreferenceScreen
+import androidx.recyclerview.widget.RecyclerView
+
+/** Base class for Settings to use PreferenceFragmentCompat */
+open abstract class SettingsBasePreferenceFragment : PreferenceFragmentCompat() {
+
+ override fun onCreateAdapter(preferenceScreen: PreferenceScreen): RecyclerView.Adapter<*> {
+ if (SettingsThemeHelper.isExpressiveTheme(requireContext()))
+ return SettingsPreferenceGroupAdapter(preferenceScreen)
+ return super.onCreateAdapter(preferenceScreen)
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
new file mode 100644
index 000000000000..98b7f7664b1b
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
@@ -0,0 +1,206 @@
+/*
+ * 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.settingslib.widget
+
+import android.os.Handler
+import android.os.Looper
+import androidx.annotation.DrawableRes
+import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
+import androidx.preference.PreferenceGroup
+import androidx.preference.PreferenceGroupAdapter
+import androidx.preference.PreferenceViewHolder
+import com.android.settingslib.widget.theme.R
+
+/**
+ * A custom adapter for displaying settings preferences in a list, handling rounded corners
+ * for preference items within a group.
+ */
+open class SettingsPreferenceGroupAdapter @JvmOverloads constructor(
+ preferenceGroup: PreferenceGroup
+) : PreferenceGroupAdapter(preferenceGroup) {
+
+ private val mPreferenceGroup = preferenceGroup
+ private var mRoundCornerMappingList: ArrayList<Int> = ArrayList()
+
+ private var mNormalPaddingStart = 0
+ private var mGroupPaddingStart = 0
+ private var mNormalPaddingEnd = 0
+ private var mGroupPaddingEnd = 0
+
+ private val mHandler = Handler(Looper.getMainLooper())
+
+ private val syncRunnable = Runnable { updatePreferences() }
+
+ init {
+ val context = preferenceGroup.context
+ mNormalPaddingStart =
+ context.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_space_small1)
+ mGroupPaddingStart = mNormalPaddingStart * 2
+ mNormalPaddingEnd =
+ context.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_space_small1)
+ mGroupPaddingEnd = mNormalPaddingEnd * 2
+ updatePreferences()
+ }
+
+ override fun onPreferenceHierarchyChange(preference: Preference) {
+ super.onPreferenceHierarchyChange(preference)
+
+ // Post after super class has posted their sync runnable to update preferences.
+ mHandler.removeCallbacks(syncRunnable)
+ mHandler.post(syncRunnable)
+ }
+
+ override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) {
+ super.onBindViewHolder(holder, position)
+ updateBackground(holder, position)
+ }
+
+ private fun updatePreferences() {
+ val oldList = ArrayList(mRoundCornerMappingList)
+ mRoundCornerMappingList = ArrayList()
+ mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup)
+ if (mRoundCornerMappingList != oldList) {
+ notifyDataSetChanged()
+ }
+ }
+
+ private fun mappingPreferenceGroup(cornerStyles: MutableList<Int>, group: PreferenceGroup) {
+ cornerStyles.clear()
+ cornerStyles.addAll(MutableList(itemCount) { 0 })
+
+ // the first item in to group
+ var startIndex = -1
+ // the last item in the group
+ var endIndex = -1
+ var currentParent: PreferenceGroup? = group
+ for (i in 0 until itemCount) {
+ when (val pref = getItem(i)) {
+ // the preference has round corner background, so we don't need to handle it.
+ is GroupSectionDividerMixin -> {
+ cornerStyles[i] = 0
+ startIndex = -1
+ endIndex = -1
+ }
+
+ // PreferenceCategory should not have round corner background.
+ is PreferenceCategory -> {
+ cornerStyles[i] = 0
+ startIndex = -1
+ endIndex = -1
+ currentParent = pref
+ }
+
+ // ExpandablePreference is PreferenceGroup but it should handle round corner
+ is Expandable -> {
+ // When ExpandablePreference is expanded, we treat is as the first item.
+ if (pref.isExpanded()) {
+ currentParent = pref as? PreferenceGroup
+ startIndex = i
+ cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP or ROUND_CORNER_CENTER
+ endIndex = -1
+ }
+ }
+
+ else -> {
+ val parent = pref?.parent
+
+ // item in the group should have round corner background.
+ cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_CENTER
+ if (parent === currentParent) {
+ // find the first item in the group
+ if (startIndex == -1) {
+ startIndex = i
+ cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP
+ }
+
+ // find the last item in the group, if we find the new last item, we should
+ // remove the old last item round corner.
+ if (endIndex == -1 || endIndex < i) {
+ if (endIndex != -1) {
+ cornerStyles[endIndex] =
+ cornerStyles[endIndex] and ROUND_CORNER_BOTTOM.inv()
+ }
+ endIndex = i
+ cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_BOTTOM
+ }
+ } else {
+ // this item is new group, we should reset the index.
+ currentParent = parent
+ startIndex = i
+ cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP
+ endIndex = i
+ cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_BOTTOM
+ }
+ }
+ }
+ }
+ }
+
+ /** handle roundCorner background */
+ private fun updateBackground(holder: PreferenceViewHolder, position: Int) {
+ @DrawableRes val backgroundRes = getRoundCornerDrawableRes(position, false /* isSelected*/)
+
+ val v = holder.itemView
+ val paddingStart = if (backgroundRes == 0) mNormalPaddingStart else mGroupPaddingStart
+ val paddingEnd = if (backgroundRes == 0) mNormalPaddingEnd else mGroupPaddingEnd
+
+ v.setPaddingRelative(paddingStart, v.paddingTop, paddingEnd, v.paddingBottom)
+ v.setBackgroundResource(backgroundRes)
+ }
+
+ @DrawableRes
+ protected fun getRoundCornerDrawableRes(position: Int, isSelected: Boolean): Int {
+ val cornerType = mRoundCornerMappingList[position]
+
+ if ((cornerType and ROUND_CORNER_CENTER) == 0) {
+ return 0
+ }
+
+ return when {
+ (cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) == 0 -> {
+ // the first
+ if (isSelected) R.drawable.settingslib_round_background_top_selected
+ else R.drawable.settingslib_round_background_top
+ }
+
+ (cornerType and ROUND_CORNER_BOTTOM) != 0 && (cornerType and ROUND_CORNER_TOP) == 0 -> {
+ // the last
+ if (isSelected) R.drawable.settingslib_round_background_bottom_selected
+ else R.drawable.settingslib_round_background_bottom
+ }
+
+ (cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) != 0 -> {
+ // the only one preference
+ if (isSelected) R.drawable.settingslib_round_background_selected
+ else R.drawable.settingslib_round_background
+ }
+
+ else -> {
+ // in the center
+ if (isSelected) R.drawable.settingslib_round_background_center_selected
+ else R.drawable.settingslib_round_background_center
+ }
+ }
+ }
+
+ companion object {
+ private const val ROUND_CORNER_CENTER: Int = 1
+ private const val ROUND_CORNER_TOP: Int = 1 shl 1
+ private const val ROUND_CORNER_BOTTOM: Int = 1 shl 2
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index dfd296fe006f..8636524ed23c 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -23,6 +23,7 @@ import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
import com.android.settingslib.spa.gallery.banner.BannerPageProvider
+import com.android.settingslib.spa.gallery.card.CardPageProvider
import com.android.settingslib.spa.gallery.chart.ChartPageProvider
import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
import com.android.settingslib.spa.gallery.dialog.NavDialogProvider
@@ -109,6 +110,7 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
TopIntroPreferencePageProvider,
CheckBoxPreferencePageProvider,
TwoTargetButtonPreferencePageProvider,
+ CardPageProvider,
),
rootPages = listOf(
HomePageProvider.createSettingsPage(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
new file mode 100644
index 000000000000..5659e2f33156
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2023 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.settingslib.spa.gallery.card
+
+import android.os.Bundle
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Stars
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.card.SuggestionCard
+import com.android.settingslib.spa.widget.card.SuggestionCardModel
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+object CardPageProvider : SettingsPageProvider {
+ override val name = "Card"
+
+ override fun getTitle(arguments: Bundle?) = TITLE
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ RegularScaffold(title = TITLE) {
+ SuggestionCard()
+ SuggestionCardWithLongTitle()
+ SuggestionCardDismissible()
+ }
+ }
+
+ @Composable
+ private fun SuggestionCard() {
+ SuggestionCard(
+ SuggestionCardModel(
+ title = "Suggestion card",
+ description = "Suggestion card description",
+ imageVector = Icons.Filled.Stars,
+ )
+ )
+ }
+
+ @Composable
+ private fun SuggestionCardWithLongTitle() {
+ SuggestionCard(
+ SuggestionCardModel(
+ title = "Top level suggestion card with a really, really long title",
+ imageVector = Icons.Filled.Stars,
+ onClick = {},
+ )
+ )
+ }
+
+ @Composable
+ private fun SuggestionCardDismissible() {
+ var isVisible by rememberSaveable { mutableStateOf(true) }
+ SuggestionCard(
+ SuggestionCardModel(
+ title = "Suggestion card",
+ description = "Suggestion card description",
+ imageVector = Icons.Filled.Stars,
+ onDismiss = { isVisible = false },
+ isVisible = isVisible,
+ )
+ )
+ }
+
+ @Composable
+ fun Entry() {
+ Preference(
+ object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ }
+ )
+ }
+
+ private const val TITLE = "Sample Card"
+}
+
+@Preview
+@Composable
+private fun CardPagePreview() {
+ SettingsTheme { CardPageProvider.Page(null) }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index 4d77ea173a85..ebfc0c536868 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -27,6 +27,7 @@ import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.gallery.banner.BannerPageProvider
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
+import com.android.settingslib.spa.gallery.card.CardPageProvider
import com.android.settingslib.spa.gallery.chart.ChartPageProvider
import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
@@ -80,6 +81,7 @@ object HomePageProvider : SettingsPageProvider {
DialogMainPageProvider.Entry()
EditorMainPageProvider.Entry()
BannerPageProvider.Entry()
+ CardPageProvider.Entry()
CopyablePageProvider.Entry()
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index 395748384b85..08bedf99519d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -28,6 +28,7 @@ object SettingsDimension {
val paddingExtraSmall6 = 12.dp
val paddingLarge = 16.dp
val paddingExtraLarge = 24.dp
+ val paddingExtraLarge1 = 28.dp
val spinnerHorizontalPadding = paddingExtraLarge
val spinnerVerticalPadding = paddingLarge
@@ -37,6 +38,7 @@ object SettingsDimension {
val actionIconPadding = 4.dp
val itemIconSize = 24.dp
+ val itemIconContainerSizeSmall = 40.dp
val itemIconContainerSize = 72.dp
val itemPaddingStart = if (isSpaExpressiveEnabled) paddingLarge else paddingExtraLarge
val itemPaddingEnd = paddingLarge
@@ -47,6 +49,12 @@ object SettingsDimension {
end = itemPaddingEnd,
bottom = itemPaddingVertical,
)
+ val footerItemPadding = PaddingValues(
+ start = paddingExtraLarge1,
+ top = itemPaddingVertical,
+ end = itemPaddingEnd,
+ bottom = itemPaddingVertical,
+ )
val textFieldPadding = PaddingValues(
start = itemPaddingStart,
end = itemPaddingEnd,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
index 86ba6864574c..61607bc8ae8a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsShape.kt
@@ -29,4 +29,6 @@ object SettingsShape {
val CornerLarge = RoundedCornerShape(24.dp)
val CornerExtraLarge = RoundedCornerShape(28.dp)
+
+ val CornerExtraLarge1 = RoundedCornerShape(40.dp)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SuggestionCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SuggestionCard.kt
new file mode 100644
index 000000000000..2126634ebd4d
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SuggestionCard.kt
@@ -0,0 +1,166 @@
+/*
+ * 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.settingslib.spa.widget.card
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.Stars
+import androidx.compose.material.icons.outlined.Stars
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsShape
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.theme.toSemiBoldWeight
+
+data class SuggestionCardModel(
+ val title: String,
+ val description: String? = null,
+ val imageVector: ImageVector,
+
+ /**
+ * A dismiss button will be displayed if this is not null.
+ *
+ * And this callback will be called when user clicks the button.
+ */
+ val onDismiss: (() -> Unit)? = null,
+ val isVisible: Boolean = true,
+ val onClick: (() -> Unit)? = null,
+)
+
+@Composable
+fun SuggestionCard(model: SuggestionCardModel) {
+ AnimatedVisibility(visible = model.isVisible) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier =
+ Modifier.padding(
+ horizontal = SettingsDimension.paddingLarge,
+ vertical = SettingsDimension.paddingSmall,
+ )
+ .fillMaxWidth()
+ .heightIn(min = SettingsDimension.preferenceMinHeight)
+ .clip(SettingsShape.CornerExtraLarge1)
+ .background(MaterialTheme.colorScheme.secondaryContainer)
+ .then(model.onClick?.let { Modifier.clickable(onClick = it) } ?: Modifier)
+ .padding(SettingsDimension.paddingExtraSmall6),
+ ) {
+ SuggestionCardIcon(model.imageVector)
+ Spacer(Modifier.padding(SettingsDimension.paddingSmall))
+ Column(modifier = Modifier.weight(1f).semantics(mergeDescendants = true) {}) {
+ SuggestionCardTitle(model.title)
+ if (model.description != null) SuggestionCardDescription(model.description)
+ }
+ if (model.onDismiss != null) {
+ Spacer(Modifier.padding(SettingsDimension.paddingSmall))
+ SuggestionCardDismissButton(model.onDismiss)
+ }
+ }
+ }
+}
+
+@Composable
+private fun SuggestionCardIcon(imageVector: ImageVector) {
+ Box(
+ modifier =
+ Modifier.padding(SettingsDimension.paddingSmall)
+ .size(SettingsDimension.itemIconContainerSizeSmall)
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.secondary),
+ contentAlignment = Alignment.Center,
+ ) {
+ Icon(
+ imageVector = imageVector,
+ contentDescription = null,
+ modifier = Modifier.size(SettingsDimension.itemIconSize),
+ tint = MaterialTheme.colorScheme.onSecondary,
+ )
+ }
+}
+
+@Composable
+private fun SuggestionCardTitle(title: String) {
+ Text(
+ text = title,
+ style = MaterialTheme.typography.titleMedium.toSemiBoldWeight(),
+ modifier = Modifier.padding(vertical = SettingsDimension.paddingTiny),
+ color = MaterialTheme.colorScheme.onSecondaryContainer,
+ )
+}
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+private fun SuggestionCardDescription(description: String) {
+ Text(
+ text = description,
+ style = MaterialTheme.typography.bodySmallEmphasized,
+ modifier = Modifier.padding(vertical = SettingsDimension.paddingTiny),
+ color = MaterialTheme.colorScheme.onSecondaryContainer,
+ )
+}
+
+@Composable
+private fun SuggestionCardDismissButton(onDismiss: () -> Unit) {
+ IconButton(shape = CircleShape, onClick = onDismiss) {
+ Icon(
+ imageVector = Icons.Filled.Close,
+ contentDescription =
+ stringResource(androidx.compose.material3.R.string.m3c_snackbar_dismiss),
+ modifier = Modifier.size(SettingsDimension.itemIconSize),
+ tint = MaterialTheme.colorScheme.onSecondaryContainer,
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun SuggestionCardPreview() {
+ SettingsTheme {
+ SuggestionCard(
+ SuggestionCardModel(
+ title = "Suggestion card",
+ description = "Suggestion card description",
+ imageVector = Icons.Outlined.Stars,
+ onDismiss = {},
+ onClick = {},
+ )
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
index acbdec0b30aa..66680fa547b1 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
@@ -49,7 +49,9 @@ fun CategoryTitle(title: String) {
text = title,
modifier =
Modifier.padding(
- start = SettingsDimension.itemPaddingStart,
+ start =
+ if (isSpaExpressiveEnabled) SettingsDimension.paddingSmall
+ else SettingsDimension.itemPaddingStart,
top = 20.dp,
end =
if (isSpaExpressiveEnabled) SettingsDimension.paddingSmall
@@ -67,16 +69,16 @@ fun CategoryTitle(title: String) {
*/
@Composable
fun Category(title: String? = null, content: @Composable ColumnScope.() -> Unit) {
+ var displayTitle by remember { mutableStateOf(false) }
Column(
modifier =
- if (isSpaExpressiveEnabled)
+ if (isSpaExpressiveEnabled && displayTitle)
Modifier.padding(
horizontal = SettingsDimension.paddingLarge,
vertical = SettingsDimension.paddingSmall,
)
else Modifier
) {
- var displayTitle by remember { mutableStateOf(false) }
if (title != null && displayTitle) CategoryTitle(title = title)
Column(
modifier =
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsBannerTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsBannerTest.kt
index a8479b01a861..a8479b01a861 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsBannerTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsBannerTest.kt
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleBannerTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsCollapsibleBannerTest.kt
index 1080fdea9455..1080fdea9455 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleBannerTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/banner/SettingsCollapsibleBannerTest.kt
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SuggestionCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SuggestionCardTest.kt
new file mode 100644
index 000000000000..96bfb3d71642
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SuggestionCardTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2023 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.settingslib.spa.widget.card
+
+import android.content.Context
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Star
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.isNotDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SuggestionCardTest {
+ @get:Rule val composeTestRule = createComposeRule()
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Test
+ fun suggestionCard_contentDisplayed() {
+ setContent()
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+ composeTestRule.onNodeWithText(DESCRIPTION).assertIsDisplayed()
+ }
+
+ @Test
+ fun suggestionCard_dismiss() {
+ setContent()
+ composeTestRule
+ .onNodeWithContentDescription(
+ context.getString(androidx.compose.material3.R.string.m3c_snackbar_dismiss)
+ )
+ .performClick()
+
+ composeTestRule.onNodeWithText(TITLE).isNotDisplayed()
+ composeTestRule.onNodeWithText(DESCRIPTION).isNotDisplayed()
+ }
+
+ private fun setContent() {
+ composeTestRule.setContent {
+ var isVisible by rememberSaveable { mutableStateOf(true) }
+ SuggestionCard(
+ SuggestionCardModel(
+ title = TITLE,
+ description = DESCRIPTION,
+ imageVector = Icons.Outlined.Star,
+ isVisible = isVisible,
+ onDismiss = { isVisible = false },
+ )
+ )
+ }
+ }
+
+ private companion object {
+ const val TITLE = "Title"
+ const val DESCRIPTION = "Description"
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index f306918ec72f..d89d3977cac3 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -39,6 +39,8 @@ import androidx.compose.ui.unit.Dp
import com.android.settingslib.development.DevelopmentSettingsEnabler
import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
+import com.android.settingslib.spa.widget.preference.IntroAppPreference
import com.android.settingslib.spa.widget.ui.CopyableBody
import com.android.settingslib.spa.widget.ui.SettingsBody
import com.android.settingslib.spa.widget.ui.SettingsTitle
@@ -48,23 +50,53 @@ import com.android.settingslib.spaprivileged.model.app.rememberAppRepository
class AppInfoProvider(private val packageInfo: PackageInfo) {
@Composable
fun AppInfo(displayVersion: Boolean = false, isClonedAppPage: Boolean = false) {
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .padding(
- horizontal = SettingsDimension.itemPaddingStart,
- vertical = SettingsDimension.itemPaddingVertical,
- )
- .semantics(mergeDescendants = true) {},
- horizontalAlignment = Alignment.CenterHorizontally,
- ) {
+ if (isSpaExpressiveEnabled) {
+ val appRepository = rememberAppRepository()
val app = checkNotNull(packageInfo.applicationInfo)
- Box(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) {
- AppIcon(app = app, size = SettingsDimension.appIconInfoSize)
+ val title = appRepository.produceLabel(app, isClonedAppPage).value
+
+ val descriptions = mutableListOf<String>()
+ if (app.isInstantApp) {
+ descriptions.add(
+ stringResource(
+ com.android.settingslib.widget.preference.app.R.string.install_type_instant
+ )
+ )
+ }
+ if (displayVersion) {
+ val versionName = packageInfo.versionNameBidiWrapped
+ if (versionName != null) descriptions.add(versionName)
+ }
+
+ IntroAppPreference(
+ title = title,
+ descriptions = descriptions,
+ appIcon = {
+ Image(
+ painter = rememberDrawablePainter(appRepository.produceIcon(app).value),
+ contentDescription = appRepository.produceIconContentDescription(app).value,
+ )
+ },
+ )
+ } else {
+ Column(
+ modifier =
+ Modifier.fillMaxWidth()
+ .padding(
+ horizontal = SettingsDimension.itemPaddingStart,
+ vertical = SettingsDimension.itemPaddingVertical,
+ )
+ .semantics(mergeDescendants = true) {},
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ val app = checkNotNull(packageInfo.applicationInfo)
+ Box(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) {
+ AppIcon(app = app, size = SettingsDimension.appIconInfoSize)
+ }
+ AppLabel(app, isClonedAppPage)
+ InstallType(app)
+ if (displayVersion) AppVersion()
}
- AppLabel(app, isClonedAppPage)
- InstallType(app)
- if (displayVersion) AppVersion()
}
}
@@ -89,19 +121,24 @@ class AppInfoProvider(private val packageInfo: PackageInfo) {
@Composable
fun FooterAppVersion(showPackageName: Boolean = rememberIsDevelopmentSettingsEnabled()) {
val context = LocalContext.current
- val footer = remember(packageInfo, showPackageName) {
- val list = mutableListOf<String>()
- packageInfo.versionNameBidiWrapped?.let {
- list += context.getString(R.string.version_text, it)
+ val footer =
+ remember(packageInfo, showPackageName) {
+ val list = mutableListOf<String>()
+ packageInfo.versionNameBidiWrapped?.let {
+ list += context.getString(R.string.version_text, it)
+ }
+ if (showPackageName) {
+ list += packageInfo.packageName
+ }
+ list.joinToString(separator = System.lineSeparator())
}
- if (showPackageName) {
- list += packageInfo.packageName
- }
- list.joinToString(separator = System.lineSeparator())
- }
if (footer.isBlank()) return
HorizontalDivider()
- Column(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
+ Column(
+ modifier =
+ if (isSpaExpressiveEnabled) Modifier.padding(SettingsDimension.footerItemPadding)
+ else Modifier.padding(SettingsDimension.itemPadding)
+ ) {
CopyableBody(footer)
}
}
@@ -109,9 +146,7 @@ class AppInfoProvider(private val packageInfo: PackageInfo) {
@Composable
private fun rememberIsDevelopmentSettingsEnabled(): Boolean {
val context = LocalContext.current
- return remember {
- DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context)
- }
+ return remember { DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context) }
}
private companion object {
diff --git a/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt
index 9b1ccef9dadf..62573fe18357 100644
--- a/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt
+++ b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt
@@ -32,7 +32,7 @@ class ZeroStatePreference @JvmOverloads constructor(
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
-) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin {
private val iconTint: Int = context.getColor(
com.android.settingslib.widget.theme.R.color.settingslib_materialColorOnSecondaryContainer
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 79c3ff9ce989..5a8763f4db6e 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -149,3 +149,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "audio_sharing_developer_option"
+ namespace: "cross_device_experiences"
+ description: "Gates whether to enable audio sharing developer option"
+ bug: "368401233"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index abc58ee99904..fd2a1cb14edd 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -819,6 +819,11 @@
<!-- Summary of checkbox setting that enables the terminal app. [CHAR LIMIT=64] -->
<string name="enable_terminal_summary">Enable terminal app that offers local shell access</string>
+ <!-- Title of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=32] -->
+ <string name="enable_linux_terminal_title">Linux development environment</string>
+ <!-- Summary of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=64] -->
+ <string name="enable_linux_terminal_summary">Run Linux terminal on Android</string>
+
<!-- HDCP checking title, used for debug purposes only. [CHAR LIMIT=25] -->
<string name="hdcp_checking_title">HDCP checking</string>
<!-- HDCP checking dialog title, used for debug purposes only. [CHAR LIMIT=25] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
index 6335e712f904..83ee9751329f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InputMediaDevice.java
@@ -125,7 +125,9 @@ public class InputMediaDevice extends MediaDevice {
? mProductName
: mContext.getString(R.string.media_transfer_usb_device_mic_name);
case TYPE_BLUETOOTH_SCO ->
- mContext.getString(R.string.media_transfer_bt_device_mic_name);
+ mProductName != null
+ ? mProductName
+ : mContext.getString(R.string.media_transfer_bt_device_mic_name);
default -> mContext.getString(R.string.media_transfer_this_device_name_desktop);
};
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
index 6c1cb7015225..7775b912e51d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InputMediaDeviceTest.java
@@ -44,6 +44,7 @@ public class InputMediaDeviceTest {
private static final String PRODUCT_NAME_BUILTIN_MIC = "Built-in Mic";
private static final String PRODUCT_NAME_WIRED_HEADSET = "My Wired Headset";
private static final String PRODUCT_NAME_USB_HEADSET = "My USB Headset";
+ private static final String PRODUCT_NAME_BT_HEADSET = "My Bluetooth Headset";
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -142,6 +143,21 @@ public class InputMediaDeviceTest {
MAX_VOLUME,
CURRENT_VOLUME,
IS_VOLUME_FIXED,
+ PRODUCT_NAME_BT_HEADSET);
+ assertThat(btMediaDevice).isNotNull();
+ assertThat(btMediaDevice.getName()).isEqualTo(PRODUCT_NAME_BT_HEADSET);
+ }
+
+ @Test
+ public void getName_returnCorrectName_btHeadset_nullProductName() {
+ InputMediaDevice btMediaDevice =
+ InputMediaDevice.create(
+ mContext,
+ String.valueOf(BT_HEADSET_ID),
+ AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
+ MAX_VOLUME,
+ CURRENT_VOLUME,
+ IS_VOLUME_FIXED,
null);
assertThat(btMediaDevice).isNotNull();
assertThat(btMediaDevice.getName())
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 408ed1e861c3..b385aaa5c7d9 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -924,6 +924,7 @@
<!-- Permission required for CTS test - CtsPackageManagerTestCases-->
<uses-permission android:name="android.permission.DOMAIN_VERIFICATION_AGENT" />
+ <uses-permission android:name="android.permission.VERIFICATION_AGENT" />
<!-- Permission required for Cts test - CtsInputTestCases -->
<uses-permission
diff --git a/packages/StatementService/Parser/Android.bp b/packages/StatementService/Parser/Android.bp
new file mode 100644
index 000000000000..c8af1344f4c7
--- /dev/null
+++ b/packages/StatementService/Parser/Android.bp
@@ -0,0 +1,26 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "StatementServiceParser",
+ use_resource_processor: true,
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ target_sdk_version: "29",
+}
diff --git a/packages/StatementService/Parser/AndroidManifest.xml b/packages/StatementService/Parser/AndroidManifest.xml
new file mode 100644
index 000000000000..a3a99ac0552d
--- /dev/null
+++ b/packages/StatementService/Parser/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.statementservice.parser">
+</manifest> \ No newline at end of file
diff --git a/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt
new file mode 100644
index 000000000000..4314f86fad37
--- /dev/null
+++ b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+@file:JvmName("DalComponentParser")
+
+package com.android.statementservice.parser
+
+import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB
+import android.os.PatternMatcher.PATTERN_LITERAL
+import android.os.PatternMatcher.PATTERN_PREFIX
+import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB
+
+/**
+ * Parses a DAL component matching expression to Android's {@link android.os.PatternMatcher} type
+ * and pattern. Matching expressions support the following wildcards:
+ *
+ * 1) An asterisk (*) matches zero to as many characters as possible
+ * 2) A question mark (?) matches any single character.
+ *
+ * Matching one to many characters can be done with a question mark followed by an asterisk (?+).
+ *
+ * @param expression A matching expression string from a DAL relation extension component used for
+ * matching a URI part. This must be a non-empty string and all characters in the
+ * string should be decoded.
+ *
+ * @return Returns a Pair containing a {@link android.os.PatternMatcher} type and pattern.
+ */
+fun parseMatchingExpression(expression: String): Pair<Int, String> {
+ if (expression.isNullOrEmpty()) {
+ throw IllegalArgumentException("Matching expressions cannot be an empty string")
+ }
+ var count = 0
+ var isAdvanced = expression.contains("?*")
+ val pattern = buildString {
+ for (char in expression) {
+ when (char) {
+ '*' -> {
+ if (this.endsWith('.') && !this.endsWith("\\.")) {
+ append('+')
+ } else {
+ count += 1
+ append(".*")
+ }
+ }
+ '?' -> {
+ count += 1
+ append('.')
+ }
+ '.' -> {
+ append("\\.")
+ }
+ '[', ']', '{', '}' -> {
+ if (isAdvanced) {
+ append('\\')
+ }
+ append(char)
+ }
+ else -> append(char)
+ }
+ }
+ }
+ if (count == 0) {
+ return Pair(PATTERN_LITERAL, pattern)
+ }
+ if (count == 1 && pattern.endsWith(".*")) {
+ return Pair(PATTERN_PREFIX, pattern.dropLast(2))
+ }
+ if (isAdvanced) {
+ return Pair(PATTERN_ADVANCED_GLOB, pattern)
+ }
+ return Pair(PATTERN_SIMPLE_GLOB, pattern)
+} \ No newline at end of file
diff --git a/packages/StatementService/TEST_MAPPING b/packages/StatementService/TEST_MAPPING
new file mode 100644
index 000000000000..0714c9366665
--- /dev/null
+++ b/packages/StatementService/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit" : [
+ {
+ "name": "StatementServiceTests"
+ }
+ ]
+} \ No newline at end of file
diff --git a/packages/StatementService/tests/Android.bp b/packages/StatementService/tests/Android.bp
new file mode 100644
index 000000000000..ec1bd96a0bc0
--- /dev/null
+++ b/packages/StatementService/tests/Android.bp
@@ -0,0 +1,30 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+ default_team: "trendy_team_framework_android_packages",
+}
+
+android_test {
+ name: "StatementServiceTests",
+ use_resource_processor: true,
+ test_suites: ["general-tests"],
+ srcs: ["src/**/*.kt"],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.runner",
+ "StatementServiceParser",
+ "truth",
+ ],
+}
diff --git a/packages/StatementService/tests/AndroidManifest.xml b/packages/StatementService/tests/AndroidManifest.xml
new file mode 100644
index 000000000000..bde0f953252c
--- /dev/null
+++ b/packages/StatementService/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.statementservice.test">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.statementservice.test" />
+</manifest>
diff --git a/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt
new file mode 100644
index 000000000000..44a56ec91458
--- /dev/null
+++ b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.statementservice.parser
+
+import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB
+import android.os.PatternMatcher.PATTERN_LITERAL
+import android.os.PatternMatcher.PATTERN_PREFIX
+import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class DalComponentParserTest {
+
+ @Test
+ fun parseExpressions() {
+ validateParsedExpression("foobar", PATTERN_LITERAL, "foobar")
+ validateParsedExpression("foo.bar", PATTERN_LITERAL, "foo\\.bar")
+ validateParsedExpression("foo*", PATTERN_PREFIX, "foo")
+ validateParsedExpression("*bar", PATTERN_SIMPLE_GLOB, ".*bar")
+ validateParsedExpression("foo*bar", PATTERN_SIMPLE_GLOB, "foo.*bar")
+ validateParsedExpression("foo.*bar", PATTERN_SIMPLE_GLOB, "foo\\..*bar")
+ validateParsedExpression("*foo*bar", PATTERN_SIMPLE_GLOB, ".*foo.*bar")
+ validateParsedExpression("foo?bar", PATTERN_SIMPLE_GLOB, "foo.bar")
+ validateParsedExpression("foo.?bar", PATTERN_SIMPLE_GLOB, "foo\\..bar")
+ validateParsedExpression("?bar", PATTERN_SIMPLE_GLOB, ".bar")
+ validateParsedExpression("foo?", PATTERN_SIMPLE_GLOB, "foo.")
+ validateParsedExpression("fo?b*r", PATTERN_SIMPLE_GLOB, "fo.b.*r")
+ validateParsedExpression("?*bar", PATTERN_ADVANCED_GLOB, ".+bar")
+ validateParsedExpression("foo?*bar", PATTERN_ADVANCED_GLOB, "foo.+bar")
+ validateParsedExpression("foo?*bar*", PATTERN_ADVANCED_GLOB, "foo.+bar.*")
+ validateParsedExpression("foo*?bar", PATTERN_SIMPLE_GLOB, "foo.*.bar")
+
+ // set matches are not supported in DAL
+ validateParsedExpression("foo[a-z]", PATTERN_LITERAL, "foo[a-z]")
+ validateParsedExpression("foo[a-z]+", PATTERN_LITERAL, "foo[a-z]+")
+ validateParsedExpression("foo[a-z]*", PATTERN_PREFIX, "foo[a-z]")
+ validateParsedExpression("[a-z]*bar", PATTERN_SIMPLE_GLOB, "[a-z].*bar")
+ validateParsedExpression("foo[a-z]?bar", PATTERN_SIMPLE_GLOB, "foo[a-z].bar")
+ validateParsedExpression("foo[a-z]?*bar", PATTERN_ADVANCED_GLOB, "foo\\[a-z\\].+bar")
+
+ // range matches are not supported in DAL
+ validateParsedExpression("fo{2}", PATTERN_LITERAL, "fo{2}")
+ validateParsedExpression("fo{2}+", PATTERN_LITERAL, "fo{2}+")
+ validateParsedExpression("fo{2}*", PATTERN_PREFIX, "fo{2}")
+ validateParsedExpression("fo{2}*bar", PATTERN_SIMPLE_GLOB, "fo{2}.*bar")
+ validateParsedExpression("fo{2}?*", PATTERN_ADVANCED_GLOB, "fo\\{2\\}.+")
+ validateParsedExpression("foo{2}?*bar", PATTERN_ADVANCED_GLOB, "foo\\{2\\}.+bar")
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun parseEmptyExpression() {
+ parseMatchingExpression("")
+ }
+
+ private fun validateParsedExpression(given: String, expectedType: Int, expectedFilter: String) {
+ val (type, filter) = parseMatchingExpression(given)
+ assertThat(filter).isEqualTo(expectedFilter)
+ assertThat(type).isEqualTo(expectedType)
+ assertThat(filter).isEqualTo(expectedFilter)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 28635318236d..3c560fdcadb6 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -87,7 +87,6 @@ filegroup {
srcs: [
"tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt",
"tests/src/**/systemui/broadcast/ActionReceiverTest.kt",
- "tests/src/**/systemui/doze/DozeMachineTest.java",
"tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
"tests/src/**/systemui/globalactions/GlobalActionsImeTest.java",
"tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt",
@@ -175,7 +174,6 @@ filegroup {
"tests/src/**/systemui/controls/management/ControlsEditingActivityTest.kt",
"tests/src/**/systemui/controls/management/ControlsRequestDialogTest.kt",
"tests/src/**/systemui/controls/ui/DetailDialogTest.kt",
- "tests/src/**/systemui/doze/DozeMachineTest.kt",
"tests/src/**/systemui/fontscaling/FontScalingDialogDelegateTest.kt",
"tests/src/**/systemui/keyguard/CustomizationProviderTest.kt",
"tests/src/**/systemui/globalactions/GlobalActionsColumnLayoutTest.java",
@@ -318,9 +316,6 @@ filegroup {
"tests/src/**/systemui/stylus/StylusUsiPowerStartableTest.kt",
"tests/src/**/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt",
"tests/src/**/keyguard/ClockEventControllerTest.kt",
- "tests/src/**/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt",
- "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.kt",
- "tests/src/**/keyguard/LegacyLockIconViewControllerTest.java",
"tests/src/**/systemui/animation/TransitionAnimatorTest.kt",
"tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt",
"tests/src/**/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt",
@@ -417,7 +412,6 @@ filegroup {
"tests/src/**/systemui/stylus/StylusUsiPowerUiTest.kt",
"tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt",
"tests/src/**/keyguard/KeyguardUpdateMonitorTest.java",
- "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.java",
"tests/src/**/keyguard/CarrierTextManagerTest.java",
"tests/src/**/systemui/ScreenDecorationsTest.java",
"tests/src/**/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt",
@@ -899,9 +893,9 @@ android_robolectric_test {
"androidx.compose.runtime_runtime",
],
libs: [
- "android.test.runner.stubs.system",
- "android.test.base.stubs.system",
- "android.test.mock.stubs.system",
+ "android.test.runner.impl",
+ "android.test.base.impl",
+ "android.test.mock.impl",
"truth",
],
@@ -936,9 +930,9 @@ android_robolectric_test {
"androidx.compose.runtime_runtime",
],
libs: [
- "android.test.runner.stubs.system",
- "android.test.base.stubs.system",
- "android.test.mock.stubs.system",
+ "android.test.runner.impl",
+ "android.test.base.impl",
+ "android.test.mock.impl",
"truth",
],
@@ -974,9 +968,9 @@ android_ravenwood_test {
"androidx.compose.runtime_runtime",
],
libs: [
- "android.test.runner.stubs.system",
- "android.test.base.stubs.system",
- "android.test.mock.stubs.system",
+ "android.test.runner.impl",
+ "android.test.base.impl",
+ "android.test.mock.impl",
],
auto_gen_config: true,
plugins: [
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a21a80506279..651244a9e52a 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -395,9 +395,9 @@ flag {
}
flag {
- name: "status_bar_ron_chips"
+ name: "status_bar_notification_chips"
namespace: "systemui"
- description: "Show rich ongoing notifications as chips in the status bar"
+ description: "Show promoted ongoing notifications as chips in the status bar"
bug: "361346412"
}
@@ -1481,3 +1481,29 @@ flag {
bug: "370863642"
}
+flag {
+ name: "notes_role_qs_tile"
+ namespace: "systemui"
+ description: "Enables notes role qs tile which opens default notes role app in app bubbles"
+ bug: "357863750"
+}
+
+flag {
+ name: "media_projection_request_attribution_fix"
+ namespace: "systemui"
+ description: "Ensure MediaProjection consent requests are properly attributed"
+ bug: "373581993"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "secondary_user_widget_host"
+ namespace: "systemui"
+ description: "Host communal widgets in the current secondary user on HSUM."
+ bug: "373874416"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 93a99bd948f1..18f40c98fe04 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -136,20 +136,16 @@ constructor(
)
/**
- * The timings when animating a View into an app using a spring animator.
- *
- * Important: since springs don't have fixed durations, these timings represent fractions of
- * the progress between the spring's initial value and its final value.
- *
- * TODO(b/372858592): make this a separate class explicitly using percentages.
+ * The timings when animating a View into an app using a spring animator. These timings
+ * represent fractions of the progress between the spring's initial value and its final
+ * value.
*/
val SPRING_TIMINGS =
- TransitionAnimator.Timings(
- totalDuration = 1000L,
- contentBeforeFadeOutDelay = 0L,
- contentBeforeFadeOutDuration = 800L,
- contentAfterFadeInDelay = 850L,
- contentAfterFadeInDuration = 135L,
+ TransitionAnimator.SpringTimings(
+ contentBeforeFadeOutDelay = 0f,
+ contentBeforeFadeOutDuration = 0.8f,
+ contentAfterFadeInDelay = 0.85f,
+ contentAfterFadeInDuration = 0.135f,
)
/**
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index 1d8ff77ac719..4cf264253bf8 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -52,7 +52,7 @@ class TransitionAnimator(
private val interpolators: Interpolators,
/** [springTimings] and [springInterpolators] must either both be null or both not null. */
- private val springTimings: Timings? = null,
+ private val springTimings: SpringTimings? = null,
private val springInterpolators: Interpolators? = null,
private val springParams: SpringParams = DEFAULT_SPRING_PARAMS,
) {
@@ -83,8 +83,22 @@ class TransitionAnimator(
delay: Long,
duration: Long,
): Float {
+ return getProgressInternal(
+ timings.totalDuration.toFloat(),
+ linearProgress,
+ delay.toFloat(),
+ duration.toFloat(),
+ )
+ }
+
+ private fun getProgressInternal(
+ totalDuration: Float,
+ linearProgress: Float,
+ delay: Float,
+ duration: Float,
+ ): Float {
return MathUtils.constrain(
- (linearProgress * timings.totalDuration - delay) / duration,
+ (linearProgress * totalDuration - delay) / duration,
0.0f,
1.0f,
)
@@ -367,6 +381,25 @@ class TransitionAnimator(
val contentAfterFadeInDuration: Long,
)
+ /**
+ * The timings (durations and delays) used by the multi-spring animator. These are expressed as
+ * fractions of 1, similar to how the progress of an animator can be expressed as a float value
+ * between 0 and 1.
+ */
+ class SpringTimings(
+ /** The portion of animation to wait before fading out the expanding content. */
+ val contentBeforeFadeOutDelay: Float,
+
+ /** The portion of animation during which the expanding content fades out. */
+ val contentBeforeFadeOutDuration: Float,
+
+ /** The portion of animation to wait before fading in the expanded content. */
+ val contentAfterFadeInDelay: Float,
+
+ /** The portion of animation during which the expanded content fades in. */
+ val contentAfterFadeInDuration: Float,
+ )
+
/** The interpolators used by this animator. */
data class Interpolators(
/** The interpolator used for the Y position, width, height and corner radius. */
@@ -453,8 +486,8 @@ class TransitionAnimator(
endState: State,
windowBackgroundLayer: GradientDrawable,
fadeWindowBackgroundLayer: Boolean = true,
- useSpring: Boolean = false,
drawHole: Boolean = false,
+ useSpring: Boolean = false,
): Animation {
val transitionContainer = controller.transitionContainer
val transitionContainerOverlay = transitionContainer.overlay
@@ -576,18 +609,14 @@ class TransitionAnimator(
}
override fun onAnimationEnd(animation: Animator) {
- if (DEBUG) {
- Log.d(TAG, "Animation ended")
- }
-
- // TODO(b/330672236): Post this to the main thread instead so that it does not
- // flicker with Flexiglass enabled.
- controller.onTransitionAnimationEnd(isExpandingFullyAbove)
- transitionContainerOverlay.remove(windowBackgroundLayer)
-
- if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
- openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
- }
+ onAnimationEnd(
+ controller,
+ isExpandingFullyAbove,
+ windowBackgroundLayer,
+ transitionContainerOverlay,
+ openingWindowSyncViewOverlay,
+ moveBackgroundLayerWhenAppVisibilityChanges,
+ )
}
}
)
@@ -1021,34 +1050,47 @@ class TransitionAnimator(
cornerRadii[7] = state.bottomCornerRadius
drawable.cornerRadii = cornerRadii
- val timings: Timings
val interpolators: Interpolators
+ val fadeInProgress: Float
+ val fadeOutProgress: Float
if (useSpring) {
- timings = springTimings!!
interpolators = springInterpolators!!
+ val timings = springTimings!!
+ fadeInProgress =
+ getProgressInternal(
+ totalDuration = 1f,
+ linearProgress,
+ timings.contentBeforeFadeOutDelay,
+ timings.contentBeforeFadeOutDuration,
+ )
+ fadeOutProgress =
+ getProgressInternal(
+ totalDuration = 1f,
+ linearProgress,
+ timings.contentAfterFadeInDelay,
+ timings.contentAfterFadeInDuration,
+ )
} else {
- timings = this.timings
interpolators = this.interpolators
+ fadeInProgress =
+ getProgress(
+ timings,
+ linearProgress,
+ timings.contentBeforeFadeOutDelay,
+ timings.contentBeforeFadeOutDuration,
+ )
+ fadeOutProgress =
+ getProgress(
+ timings,
+ linearProgress,
+ timings.contentAfterFadeInDelay,
+ timings.contentAfterFadeInDuration,
+ )
}
// We first fade in the background layer to hide the expanding view, then fade it out with
// SRC mode to draw a hole punch in the status bar and reveal the opening window (if
// needed). If !isLaunching, the reverse happens.
- val fadeInProgress =
- getProgress(
- timings,
- linearProgress,
- timings.contentBeforeFadeOutDelay,
- timings.contentBeforeFadeOutDuration,
- )
- val fadeOutProgress =
- getProgress(
- timings,
- linearProgress,
- timings.contentAfterFadeInDelay,
- timings.contentAfterFadeInDuration,
- )
-
if (isLaunching) {
if (fadeInProgress < 1) {
val alpha =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 96c47cc5fb6f..4ab526188ffe 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -765,7 +765,7 @@ private fun BoxScope.CommunalHubLazyGrid(
alpha = { outlineAlpha },
modifier =
Modifier.requiredSize(dpSize).thenIf(
- dragDropState.draggingItemIndex != index
+ dragDropState.draggingItemKey != item.key
) {
Modifier.animateItem(
placementSpec = spring(stiffness = Spring.StiffnessMediumLow)
@@ -778,7 +778,7 @@ private fun BoxScope.CommunalHubLazyGrid(
dragDropState = dragDropState,
selected = selected,
enabled = item.isWidgetContent(),
- index = index,
+ key = item.key,
) { isDragging ->
CommunalContent(
modifier = Modifier.fillMaxSize(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
index 101385f88825..0718bc331d41 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/GridDragDropState.kt
@@ -38,8 +38,9 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.round
import androidx.compose.ui.unit.toOffset
import androidx.compose.ui.unit.toSize
import com.android.systemui.Flags.communalWidgetResizing
@@ -93,7 +94,7 @@ internal constructor(
private val scope: CoroutineScope,
private val updateDragPositionForRemove: (offset: Offset) -> Boolean,
) {
- var draggingItemIndex by mutableStateOf<Int?>(null)
+ var draggingItemKey by mutableStateOf<Any?>(null)
private set
var isDraggingToRemove by mutableStateOf(false)
@@ -105,6 +106,8 @@ internal constructor(
private var draggingItemInitialOffset by mutableStateOf(Offset.Zero)
private var dragStartPointerOffset by mutableStateOf(Offset.Zero)
+ private var previousTargetItemKey: Any? = null
+
internal val draggingItemOffset: Offset
get() =
draggingItemLayoutInfo?.let { item ->
@@ -112,7 +115,7 @@ internal constructor(
} ?: Offset.Zero
private val draggingItemLayoutInfo: LazyGridItemInfo?
- get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggingItemIndex }
+ get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.key == draggingItemKey }
/**
* Called when dragging is initiated.
@@ -137,7 +140,7 @@ internal constructor(
.firstItemAtOffset(normalizedOffset - contentOffset)
?.apply {
dragStartPointerOffset = normalizedOffset - this.offset.toOffset()
- draggingItemIndex = index
+ draggingItemKey = key
draggingItemInitialOffset = this.offset.toOffset()
return true
}
@@ -146,16 +149,19 @@ internal constructor(
}
internal fun onDragInterrupted() {
- draggingItemIndex?.let {
+ draggingItemKey?.let {
if (isDraggingToRemove) {
- contentListState.onRemove(it)
+ contentListState.onRemove(
+ contentListState.list.indexOfFirst { it.key == draggingItemKey }
+ )
isDraggingToRemove = false
updateDragPositionForRemove(Offset.Zero)
}
// persist list editing changes on dragging ends
contentListState.onSaveList()
- draggingItemIndex = null
+ draggingItemKey = null
}
+ previousTargetItemKey = null
draggingItemDraggedDelta = Offset.Zero
draggingItemInitialOffset = Offset.Zero
dragStartPointerOffset = Offset.Zero
@@ -170,15 +176,29 @@ internal constructor(
val startOffset = draggingItem.offset.toOffset() + draggingItemOffset
val endOffset = startOffset + draggingItem.size.toSize()
val middleOffset = startOffset + (endOffset - startOffset) / 2f
+ val draggingBoundingBox =
+ IntRect(draggingItem.offset + draggingItemOffset.round(), draggingItem.size)
val targetItem =
- state.layoutInfo.visibleItemsInfo
- .asSequence()
- .filter { item -> contentListState.isItemEditable(item.index) }
- .filter { item -> draggingItem.index != item.index }
- .firstItemAtOffset(middleOffset)
+ if (communalWidgetResizing()) {
+ state.layoutInfo.visibleItemsInfo.findLast { item ->
+ val itemBoundingBox = IntRect(item.offset, item.size)
+ draggingItemKey != item.key &&
+ contentListState.isItemEditable(item.index) &&
+ draggingBoundingBox.contains(itemBoundingBox.center)
+ }
+ } else {
+ state.layoutInfo.visibleItemsInfo
+ .asSequence()
+ .filter { item -> contentListState.isItemEditable(item.index) }
+ .filter { item -> draggingItem.index != item.index }
+ .firstItemAtOffset(middleOffset)
+ }
- if (targetItem != null) {
+ if (
+ targetItem != null &&
+ (!communalWidgetResizing() || targetItem.key != previousTargetItemKey)
+ ) {
val scrollToIndex =
if (targetItem.index == state.firstVisibleItemIndex) {
draggingItem.index
@@ -187,6 +207,14 @@ internal constructor(
} else {
null
}
+ if (communalWidgetResizing()) {
+ // Keep track of the previous target item, to avoid rapidly oscillating between
+ // items if the target item doesn't visually move as a result of the index change.
+ // In this case, even after the index changes, we'd still be colliding with the
+ // element, so it would be selected as the target item the next time this function
+ // runs again, which would trigger us to revert the index change we recently made.
+ previousTargetItemKey = targetItem.key
+ }
if (scrollToIndex != null) {
scope.launch {
// this is needed to neutralize automatic keeping the first item first.
@@ -196,20 +224,17 @@ internal constructor(
} else {
contentListState.onMove(draggingItem.index, targetItem.index)
}
- draggingItemIndex = targetItem.index
isDraggingToRemove = false
- } else {
+ } else if (targetItem == null) {
val overscroll = checkForOverscroll(startOffset, endOffset)
if (overscroll != 0f) {
scrollChannel.trySend(overscroll)
}
isDraggingToRemove = checkForRemove(startOffset)
+ previousTargetItemKey = null
}
}
- private val LazyGridItemInfo.offsetEnd: IntOffset
- get() = this.offset + this.size
-
/** Calculate the amount dragged out of bound on both sides. Returns 0f if not overscrolled */
private fun checkForOverscroll(startOffset: Offset, endOffset: Offset): Float {
return when {
@@ -237,7 +262,7 @@ fun Modifier.dragContainer(
viewModel: BaseCommunalViewModel,
): Modifier {
return this.then(
- pointerInput(dragDropState, contentOffset) {
+ Modifier.pointerInput(dragDropState, contentOffset) {
detectDragGesturesAfterLongPress(
onDrag = { change, offset ->
change.consume()
@@ -273,7 +298,7 @@ fun Modifier.dragContainer(
@Composable
fun LazyGridItemScope.DraggableItem(
dragDropState: GridDragDropState,
- index: Int,
+ key: Any,
enabled: Boolean,
selected: Boolean,
modifier: Modifier = Modifier,
@@ -283,7 +308,7 @@ fun LazyGridItemScope.DraggableItem(
return content(false)
}
- val dragging = index == dragDropState.draggingItemIndex
+ val dragging = key == dragDropState.draggingItemKey
val itemAlpha: Float by
animateFloatAsState(
targetValue = if (dragDropState.isDraggingToRemove) 0.5f else 1f,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
index 9444664885c8..71230f9cde12 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
@@ -17,8 +17,9 @@
package com.android.systemui.keyguard.ui.composable.modifier
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
@@ -41,15 +42,17 @@ fun Modifier.burnInAware(
params: BurnInParameters,
isClock: Boolean = false,
): Modifier {
- val translationYState = remember { mutableStateOf(0F) }
- viewModel.updateBurnInParams(params.copy(translationY = { translationYState.value }))
+ val cachedYTranslation = remember { mutableFloatStateOf(0f) }
+ LaunchedEffect(Unit) {
+ viewModel.updateBurnInParams(params.copy(translationY = { cachedYTranslation.floatValue }))
+ }
val burnIn = viewModel.movement
val translationX by
burnIn.map { it.translationX.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
val translationY by
burnIn.map { it.translationY.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
- translationYState.value = translationY
+ cachedYTranslation.floatValue = translationY
val scaleViewModel by
burnIn
.map { BurnInScaleViewModel(scale = it.scale, scaleClockOnly = it.scaleClockOnly) }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
index 8b9e9274b448..e4c60e166fd5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
@@ -18,6 +18,8 @@ package com.android.systemui.notifications.ui.composable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.util.fastCoerceAtLeast
+import androidx.compose.ui.util.fastCoerceAtMost
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
/**
@@ -44,7 +46,7 @@ fun NotificationScrimNestedScrollConnection(
orientation = Orientation.Vertical,
// scrolling up and inner content is taller than the scrim, so scrim needs to
// expand; content can scroll once scrim is at the minScrimOffset.
- canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
+ canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
offsetAvailable < 0 &&
offsetBeforeStart == 0f &&
contentHeight() > minVisibleScrimHeight() &&
@@ -52,36 +54,38 @@ fun NotificationScrimNestedScrollConnection(
},
// scrolling down and content is done scrolling to top. After that, the scrim
// needs to collapse; collapse the scrim until it is at the maxScrimOffset.
- canStartPostScroll = { offsetAvailable, _ ->
+ canStartPostScroll = { offsetAvailable, _, _ ->
offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll())
},
canStartPostFling = { false },
- canContinueScroll = {
- val currentHeight = scrimOffset()
- minScrimOffset() < currentHeight && currentHeight < maxScrimOffset
- },
- canScrollOnFling = true,
+ canStopOnPreFling = { false },
onStart = { offsetAvailable -> onStart(offsetAvailable) },
- onScroll = { offsetAvailable ->
+ onScroll = { offsetAvailable, _ ->
val currentHeight = scrimOffset()
val amountConsumed =
if (offsetAvailable > 0) {
val amountLeft = maxScrimOffset - currentHeight
- offsetAvailable.coerceAtMost(amountLeft)
+ offsetAvailable.fastCoerceAtMost(amountLeft)
} else {
val amountLeft = minScrimOffset() - currentHeight
- offsetAvailable.coerceAtLeast(amountLeft)
+ offsetAvailable.fastCoerceAtLeast(amountLeft)
}
snapScrimOffset(currentHeight + amountConsumed)
amountConsumed
},
- // Don't consume the velocity on pre/post fling
onStop = { velocityAvailable ->
onStop(velocityAvailable)
if (scrimOffset() < minScrimOffset()) {
animateScrimOffset(minScrimOffset())
}
- { 0f }
+ // Don't consume the velocity on pre/post fling
+ 0f
+ },
+ onCancel = {
+ onStop(0f)
+ if (scrimOffset() < minScrimOffset()) {
+ animateScrimOffset(minScrimOffset())
+ }
},
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
index a706585deebc..edb05ebd77d1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
@@ -28,6 +28,7 @@ import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastCoerceAtLeast
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
import kotlin.math.max
import kotlin.math.roundToInt
@@ -86,21 +87,25 @@ fun NotificationStackNestedScrollConnection(
): PriorityNestedScrollConnection {
return PriorityNestedScrollConnection(
orientation = Orientation.Vertical,
- canStartPreScroll = { _, _ -> false },
- canStartPostScroll = { offsetAvailable, offsetBeforeStart ->
+ canStartPreScroll = { _, _, _ -> false },
+ canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ ->
offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
},
canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() },
- canContinueScroll = { stackOffset() > 0f },
- canScrollOnFling = true,
+ canStopOnPreFling = { false },
onStart = { offsetAvailable -> onStart(offsetAvailable) },
- onScroll = { offsetAvailable ->
- onScroll(offsetAvailable)
- offsetAvailable
+ onScroll = { offsetAvailable, _ ->
+ val minOffset = 0f
+ val consumed = offsetAvailable.fastCoerceAtLeast(minOffset - stackOffset())
+ if (consumed != 0f) {
+ onScroll(consumed)
+ }
+ consumed
},
onStop = { velocityAvailable ->
onStop(velocityAvailable)
- suspend { velocityAvailable }
+ velocityAvailable
},
+ onCancel = { onStop(0f) },
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index 54497f621c73..2a91bd8b1d73 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -133,7 +133,14 @@ fun SceneScope.QuickSettingsLayout(
Column(
verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding),
horizontalAlignment = Alignment.CenterHorizontally,
- modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
+ modifier =
+ modifier
+ .fillMaxWidth()
+ .padding(
+ start = QuickSettingsShade.Dimensions.Padding,
+ end = QuickSettingsShade.Dimensions.Padding,
+ top = QuickSettingsShade.Dimensions.Padding,
+ ),
) {
BrightnessSliderContainer(
viewModel = viewModel.brightnessSliderViewModel,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 085157ac72b9..9f99c372e2a8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -27,9 +27,10 @@ import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
-import com.android.compose.nestedscroll.SuspendedValue
import kotlin.math.absoluteValue
+internal typealias SuspendedValue<T> = suspend () -> T
+
internal interface DraggableHandler {
/**
* Start a drag in the given [startedPosition], with the given [overSlop] and number of
@@ -612,7 +613,7 @@ internal class NestedScrollHandlerImpl(
return PriorityNestedScrollConnection(
orientation = orientation,
- canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
+ canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
canChangeScene =
if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f
@@ -630,7 +631,13 @@ internal class NestedScrollHandlerImpl(
return@PriorityNestedScrollConnection false
}
- _lastPointersInfo = pointersInfoOwner.pointersInfo()
+ val pointersInfo = pointersInfoOwner.pointersInfo()
+
+ if (pointersInfo.isMouseWheel) {
+ // Do not support mouse wheel interactions
+ return@PriorityNestedScrollConnection false
+ }
+ _lastPointersInfo = pointersInfo
// If the current swipe transition is *not* closed to 0f or 1f, then we want the
// scroll events to intercept the current transition to continue the scene
@@ -638,7 +645,7 @@ internal class NestedScrollHandlerImpl(
isIntercepting = true
true
},
- canStartPostScroll = { offsetAvailable, offsetBeforeStart ->
+ canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ ->
val behavior: NestedScrollBehavior =
when {
offsetAvailable > 0f -> topOrLeftBehavior
@@ -649,7 +656,12 @@ internal class NestedScrollHandlerImpl(
val isZeroOffset =
if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f
- _lastPointersInfo = pointersInfoOwner.pointersInfo()
+ val pointersInfo = pointersInfoOwner.pointersInfo()
+ if (pointersInfo.isMouseWheel) {
+ // Do not support mouse wheel interactions
+ return@PriorityNestedScrollConnection false
+ }
+ _lastPointersInfo = pointersInfo
val canStart =
when (behavior) {
@@ -684,7 +696,12 @@ internal class NestedScrollHandlerImpl(
// We could start an overscroll animation
canChangeScene = false
- _lastPointersInfo = pointersInfoOwner.pointersInfo()
+ val pointersInfo = pointersInfoOwner.pointersInfo()
+ if (pointersInfo.isMouseWheel) {
+ // Do not support mouse wheel interactions
+ return@PriorityNestedScrollConnection false
+ }
+ _lastPointersInfo = pointersInfo
val canStart = behavior.canStartOnPostFling && hasNextScene(velocityAvailable)
if (canStart) {
@@ -693,8 +710,7 @@ internal class NestedScrollHandlerImpl(
canStart
},
- canContinueScroll = { true },
- canScrollOnFling = false,
+ canStopOnPreFling = { true },
onStart = { offsetAvailable ->
val pointersInfo = pointersInfo()
dragController =
@@ -704,19 +720,33 @@ internal class NestedScrollHandlerImpl(
overSlop = if (isIntercepting) 0f else offsetAvailable,
)
},
- onScroll = { offsetAvailable ->
+ onScroll = { offsetAvailable, _ ->
val controller = dragController ?: error("Should be called after onStart")
+ val pointersInfo = pointersInfoOwner.pointersInfo()
+ if (pointersInfo.isMouseWheel) {
+ // Do not support mouse wheel interactions
+ return@PriorityNestedScrollConnection 0f
+ }
+
// TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is
// initiated in a nested child.
controller.onDrag(delta = offsetAvailable)
},
onStop = { velocityAvailable ->
val controller = dragController ?: error("Should be called after onStart")
-
- controller
- .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene)
- .also { dragController = null }
+ try {
+ controller
+ .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene)
+ .invoke()
+ } finally {
+ dragController = null
+ }
+ },
+ onCancel = {
+ val controller = dragController ?: error("Should be called after onStart")
+ controller.onStop(velocity = 0f, canChangeContent = canChangeScene)
+ dragController = null
},
)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt
index ca68c256fd73..772872719ebe 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ElementMatcher.kt
@@ -22,19 +22,52 @@ interface ElementMatcher {
fun matches(key: ElementKey, content: ContentKey): Boolean
}
+/** Returns an [ElementMatcher] that matches any element in [content]. */
+fun inContent(content: ContentKey): ElementMatcher {
+ val matcherContent = content
+ return object : ElementMatcher {
+ override fun matches(key: ElementKey, content: ContentKey): Boolean {
+ return content == matcherContent
+ }
+ }
+}
+
+/** Returns an [ElementMatcher] that matches all elements not matching [this] matcher. */
+operator fun ElementMatcher.not(): ElementMatcher {
+ val delegate = this
+ return object : ElementMatcher {
+ override fun matches(key: ElementKey, content: ContentKey): Boolean {
+ return !delegate.matches(key, content)
+ }
+ }
+}
+
+/**
+ * Returns an [ElementMatcher] that matches all elements matching both [this] matcher and [other].
+ */
+infix fun ElementMatcher.and(other: ElementMatcher): ElementMatcher {
+ val delegate = this
+ return object : ElementMatcher {
+ override fun matches(key: ElementKey, content: ContentKey): Boolean {
+ return delegate.matches(key, content) && other.matches(key, content)
+ }
+ }
+}
+
/**
- * Returns an [ElementMatcher] that matches elements in [content] also matching [this]
- * [ElementMatcher].
+ * Returns an [ElementMatcher] that matches all elements either [this] matcher, or [other], or both.
*/
-fun ElementMatcher.inContent(content: ContentKey): ElementMatcher {
+infix fun ElementMatcher.or(other: ElementMatcher): ElementMatcher {
val delegate = this
- val matcherScene = content
return object : ElementMatcher {
override fun matches(key: ElementKey, content: ContentKey): Boolean {
- return content == matcherScene && delegate.matches(key, content)
+ return delegate.matches(key, content) || other.matches(key, content)
}
}
}
-@Deprecated("Use inContent() instead", replaceWith = ReplaceWith("inContent(scene)"))
-fun ElementMatcher.inScene(scene: SceneKey) = inContent(scene)
+@Deprecated(
+ "Use `this and inContent()` instead",
+ replaceWith = ReplaceWith("this and inContent(scene)"),
+)
+fun ElementMatcher.inScene(scene: SceneKey) = this and inContent(scene)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index aa70a0ce156b..8613f6da0f62 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -29,6 +29,7 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.pointer.AwaitPointerEventScope
import androidx.compose.ui.input.pointer.PointerEvent
import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.PointerId
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.PointerInputScope
@@ -184,6 +185,7 @@ internal class MultiPointerDraggableNode(
private var startedPosition: Offset? = null
private var pointersDown: Int = 0
+ private var isMouseWheel: Boolean = false
internal fun pointersInfo(): PointersInfo {
return PointersInfo(
@@ -191,6 +193,7 @@ internal class MultiPointerDraggableNode(
startedPosition = startedPosition,
// We could have 0 pointers during fling or for other reasons.
pointersDown = pointersDown.coerceAtLeast(1),
+ isMouseWheel = isMouseWheel,
)
}
@@ -202,8 +205,15 @@ internal class MultiPointerDraggableNode(
// [requireAncestorPointersInfoOwner], to our descendants.
while (currentContext.isActive) {
// During the Initial pass, we receive the event after our ancestors.
- val changes = awaitPointerEvent(PointerEventPass.Initial).changes
+ val pointerEvent = awaitPointerEvent(PointerEventPass.Initial)
+
+ // Ignore cursor has entered the input region.
+ // This will only be sent after the cursor is hovering when in the input region.
+ if (pointerEvent.type == PointerEventType.Enter) continue
+
+ val changes = pointerEvent.changes
pointersDown = changes.countDown()
+ isMouseWheel = pointerEvent.type == PointerEventType.Scroll
when {
// There are no more pointers down.
@@ -223,7 +233,8 @@ internal class MultiPointerDraggableNode(
// The first pointer down, startedPosition was not set.
startedPosition == null -> {
- val firstPointerDown = changes.single()
+ // Mouse wheel could start with multiple pointer down
+ val firstPointerDown = changes.first()
velocityPointerId = firstPointerDown.id
velocityTracker.resetTracking()
velocityTracker.addPointerInputChange(firstPointerDown)
@@ -647,4 +658,8 @@ internal fun interface PointersInfoOwner {
fun pointersInfo(): PointersInfo
}
-internal data class PointersInfo(val startedPosition: Offset?, val pointersDown: Int)
+internal data class PointersInfo(
+ val startedPosition: Offset?,
+ val pointersDown: Int,
+ val isMouseWheel: Boolean,
+)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 2657d7cf8156..3c3c612c028b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -430,6 +430,10 @@ internal class MutableSceneTransitionLayoutStateImpl(
// Replace the transition.
transitionStates =
transitionStates.subList(0, transitionStates.lastIndex) + transition
+
+ // Make sure it is removed from the finishedTransitions set if it was already
+ // finished.
+ finishedTransitions.remove(currentState)
} else {
// Append the new transition.
transitionStates = transitionStates + transition
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index 205267da151a..f0043e1e89b0 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -27,7 +27,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
-import com.android.compose.nestedscroll.SuspendedValue
import kotlin.math.absoluteValue
import kotlinx.coroutines.CompletableDeferred
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index 061583a64702..a3f2a434cff7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -81,16 +81,16 @@ private class SwipeToSceneRootNode(
draggableHandler: DraggableHandlerImpl,
swipeDetector: SwipeDetector,
) : DelegatingNode() {
- private var delegate = delegate(SwipeToSceneNode(draggableHandler, swipeDetector))
+ private var delegateNode = delegate(SwipeToSceneNode(draggableHandler, swipeDetector))
fun update(draggableHandler: DraggableHandlerImpl, swipeDetector: SwipeDetector) {
- if (draggableHandler == delegate.draggableHandler) {
+ if (draggableHandler == delegateNode.draggableHandler) {
// Simple update, just update the swipe detector directly and keep the node.
- delegate.swipeDetector = swipeDetector
+ delegateNode.swipeDetector = swipeDetector
} else {
// The draggableHandler changed, force recreate the underlying SwipeToSceneNode.
- undelegate(delegate)
- delegate = delegate(SwipeToSceneNode(draggableHandler, swipeDetector))
+ undelegate(delegateNode)
+ delegateNode = delegate(SwipeToSceneNode(draggableHandler, swipeDetector))
}
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
index 4ae323517b26..ecf64b771d1f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
@@ -18,6 +18,8 @@ package com.android.compose.nestedscroll
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.util.fastCoerceAtLeast
+import androidx.compose.ui.util.fastCoerceAtMost
/**
* A [NestedScrollConnection] that listens for all vertical scroll events and responds in the
@@ -43,35 +45,32 @@ fun LargeTopAppBarNestedScrollConnection(
orientation = Orientation.Vertical,
// When swiping up, the LargeTopAppBar will shrink (to [minHeight]) and the content will
// expand. Then, you can then scroll down the content.
- canStartPreScroll = { offsetAvailable, offsetBeforeStart ->
+ canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
offsetAvailable < 0 && offsetBeforeStart == 0f && height() > minHeight()
},
// When swiping down, the content will scroll up until it reaches the top. Then, the
// LargeTopAppBar will expand until it reaches its [maxHeight].
- canStartPostScroll = { offsetAvailable, _ ->
+ canStartPostScroll = { offsetAvailable, _, _ ->
offsetAvailable > 0 && height() < maxHeight()
},
canStartPostFling = { false },
- canContinueScroll = {
- val currentHeight = height()
- minHeight() < currentHeight && currentHeight < maxHeight()
- },
- canScrollOnFling = true,
+ canStopOnPreFling = { false },
onStart = { /* do nothing */ },
- onScroll = { offsetAvailable ->
+ onScroll = { offsetAvailable, _ ->
val currentHeight = height()
val amountConsumed =
if (offsetAvailable > 0) {
val amountLeft = maxHeight() - currentHeight
- offsetAvailable.coerceAtMost(amountLeft)
+ offsetAvailable.fastCoerceAtMost(amountLeft)
} else {
val amountLeft = minHeight() - currentHeight
- offsetAvailable.coerceAtLeast(amountLeft)
+ offsetAvailable.fastCoerceAtLeast(amountLeft)
}
onHeightChanged(currentHeight + amountConsumed)
amountConsumed
},
// Don't consume the velocity on pre/post fling
- onStop = { { 0f } },
+ onStop = { 0f },
+ onCancel = { /* do nothing */ },
)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index a3641e6635e7..636c55799ff2 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -16,37 +16,59 @@
package com.android.compose.nestedscroll
+import androidx.compose.animation.core.AnimationState
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.animation.core.animateDecay
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.Velocity
import com.android.compose.ui.util.SpaceVectorConverter
+import kotlin.math.abs
import kotlin.math.sign
-
-internal typealias SuspendedValue<T> = suspend () -> T
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.async
+import kotlinx.coroutines.coroutineScope
/**
- * This [NestedScrollConnection] waits for a child to scroll ([onPreScroll] or [onPostScroll]), and
- * then decides (via [canStartPreScroll] or [canStartPostScroll]) if it should take over scrolling.
- * If it does, it will scroll before its children, until [canContinueScroll] allows it.
+ * A [NestedScrollConnection] that intercepts scroll events in priority mode.
*
- * Note: Call [reset] before destroying this object to make sure you always get a call to [onStop]
- * after [onStart].
+ * Priority mode allows this connection to take control over scroll events within a nested scroll
+ * hierarchy. When in priority mode, this connection consumes scroll events before its children,
+ * enabling custom scrolling behaviors like sticky headers.
*
+ * @param orientation The orientation of the scroll.
+ * @param canStartPreScroll lambda that returns true if the connection can start consuming scroll
+ * events in pre-scroll mode.
+ * @param canStartPostScroll lambda that returns true if the connection can start consuming scroll
+ * events in post-scroll mode.
+ * @param canStartPostFling lambda that returns true if the connection can start consuming scroll
+ * events in post-fling mode.
+ * @param canStopOnPreFling lambda that returns true if the connection can stop consuming scroll
+ * events in pre-fling (i.e. as soon as the user lifts their fingers).
+ * @param onStart lambda that is called when the connection starts consuming scroll events.
+ * @param onScroll lambda that is called when the connection consumes a scroll event and returns the
+ * consumed amount.
+ * @param onStop lambda that is called when the connection stops consuming scroll events and returns
+ * the consumed velocity.
+ * @param onCancel lambda that is called when the connection is cancelled.
* @sample LargeTopAppBarNestedScrollConnection
* @sample com.android.compose.animation.scene.NestedScrollHandlerImpl.nestedScrollConnection
*/
class PriorityNestedScrollConnection(
orientation: Orientation,
- private val canStartPreScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
- private val canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean,
+ private val canStartPreScroll:
+ (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean,
+ private val canStartPostScroll:
+ (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean,
private val canStartPostFling: (velocityAvailable: Float) -> Boolean,
- private val canContinueScroll: (source: NestedScrollSource) -> Boolean,
- private val canScrollOnFling: Boolean,
+ private val canStopOnPreFling: () -> Boolean,
private val onStart: (offsetAvailable: Float) -> Unit,
- private val onScroll: (offsetAvailable: Float) -> Float,
- private val onStop: (velocityAvailable: Float) -> SuspendedValue<Float>,
+ private val onScroll: (offsetAvailable: Float, source: NestedScrollSource) -> Float,
+ private val onStop: suspend (velocityAvailable: Float) -> Float,
+ private val onCancel: () -> Unit,
) : NestedScrollConnection, SpaceVectorConverter by SpaceVectorConverter(orientation) {
/** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */
@@ -54,6 +76,9 @@ class PriorityNestedScrollConnection(
private var offsetScrolledBeforePriorityMode = 0f
+ /** This job allows us to interrupt the onStop animation */
+ private var onStopJob: Deferred<Float> = CompletableDeferred(0f)
+
override fun onPostScroll(
consumed: Offset,
available: Offset,
@@ -64,62 +89,48 @@ class PriorityNestedScrollConnection(
// the beginning or from the last fling gesture.
val offsetBeforeStart = offsetScrolledBeforePriorityMode - availableFloat
- if (
- isPriorityMode ||
- (source == NestedScrollSource.SideEffect && !canScrollOnFling) ||
- !canStartPostScroll(availableFloat, offsetBeforeStart)
- ) {
+ if (isPriorityMode || !canStartPostScroll(availableFloat, offsetBeforeStart, source)) {
// The priority mode cannot start so we won't consume the available offset.
return Offset.Zero
}
- return onPriorityStart(availableFloat).toOffset()
+ return start(availableFloat, source).toOffset()
}
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
if (!isPriorityMode) {
- if (source == NestedScrollSource.UserInput || canScrollOnFling) {
- val availableFloat = available.toFloat()
- if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode)) {
- return onPriorityStart(availableFloat).toOffset()
- }
- // We want to track the amount of offset consumed before entering priority mode
- offsetScrolledBeforePriorityMode += availableFloat
+ val availableFloat = available.toFloat()
+ if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode, source)) {
+ return start(availableFloat, source).toOffset()
}
-
- return Offset.Zero
- }
-
- val availableFloat = available.toFloat()
- if (!canContinueScroll(source)) {
- // Step 3a: We have lost priority and we no longer need to intercept scroll events.
- onPriorityStop(velocity = 0f)
-
- // We've just reset offsetScrolledBeforePriorityMode to 0f
// We want to track the amount of offset consumed before entering priority mode
offsetScrolledBeforePriorityMode += availableFloat
-
return Offset.Zero
}
- // Step 2: We have the priority and can consume the scroll events.
- return onScroll(availableFloat).toOffset()
+ return scroll(available.toFloat(), source).toOffset()
}
override suspend fun onPreFling(available: Velocity): Velocity {
- if (isPriorityMode && canScrollOnFling) {
- // We don't want to consume the velocity, we prefer to continue receiving scroll events.
+ if (!isPriorityMode) {
+ resetOffsetTracker()
return Velocity.Zero
}
- // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the speed
- // of the fling gesture.
- return onPriorityStop(velocity = available.toFloat()).invoke().toVelocity()
+
+ if (canStopOnPreFling()) {
+ // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the
+ // velocity of the fling gesture.
+ return stop(velocityAvailable = available.toFloat()).toVelocity()
+ }
+
+ // We don't want to consume the velocity, we prefer to continue receiving scroll events.
+ return Velocity.Zero
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
val availableFloat = available.toFloat()
if (isPriorityMode) {
- return onPriorityStop(velocity = availableFloat).invoke().toVelocity()
+ return stop(velocityAvailable = availableFloat).toVelocity()
}
if (!canStartPostFling(availableFloat)) {
@@ -131,10 +142,14 @@ class PriorityNestedScrollConnection(
// TODO(b/291053278): Remove canStartPostFling() and instead make it possible to define the
// overscroll behavior on the Scene level.
val smallOffset = availableFloat.sign
- onPriorityStart(availableOffset = smallOffset)
+ start(
+ availableOffset = smallOffset,
+ source = NestedScrollSource.SideEffect,
+ skipScroll = true,
+ )
// This is the last event of a scroll gesture.
- return onPriorityStop(availableFloat).invoke().toVelocity()
+ return stop(availableFloat).toVelocity()
}
/**
@@ -143,36 +158,76 @@ class PriorityNestedScrollConnection(
* TODO(b/303224944) This method should be removed.
*/
fun reset() {
- // Step 3c: To ensure that an onStop is always called for every onStart.
- onPriorityStop(velocity = 0f)
+ if (isPriorityMode) {
+ // Step 3c: To ensure that an onStop (or onCancel) is always called for every onStart.
+ cancel()
+ } else {
+ resetOffsetTracker()
+ }
}
- private fun onPriorityStart(availableOffset: Float): Float {
- if (isPriorityMode) {
- error("This should never happen, onPriorityStart() was called when isPriorityMode")
+ private fun shouldStop(consumed: Float): Boolean {
+ return consumed == 0f
+ }
+
+ private fun start(
+ availableOffset: Float,
+ source: NestedScrollSource,
+ skipScroll: Boolean = false,
+ ): Float {
+ check(!isPriorityMode) {
+ "This should never happen, start() was called when isPriorityMode"
}
// Step 1: It's our turn! We start capturing scroll events when one of our children has an
// available offset following a scroll event.
isPriorityMode = true
+ onStopJob.cancel()
+
// Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is
// lifted (step 3b), or this object has been destroyed (step 3c).
onStart(availableOffset)
- return onScroll(availableOffset)
+ return if (skipScroll) 0f else scroll(availableOffset, source)
}
- private fun onPriorityStop(velocity: Float): SuspendedValue<Float> {
- // We can restart tracking the consumed offsets from scratch.
- offsetScrolledBeforePriorityMode = 0f
+ private fun scroll(offsetAvailable: Float, source: NestedScrollSource): Float {
+ // Step 2: We have the priority and can consume the scroll events.
+ val consumedByScroll = onScroll(offsetAvailable, source)
- if (!isPriorityMode) {
- return { 0f }
+ if (shouldStop(consumedByScroll)) {
+ // Step 3a: We have lost priority and we no longer need to intercept scroll events.
+ cancel()
+
+ // We've just reset offsetScrolledBeforePriorityMode to 0f
+ // We want to track the amount of offset consumed before entering priority mode
+ offsetScrolledBeforePriorityMode += offsetAvailable - consumedByScroll
}
+ return consumedByScroll
+ }
+
+ /** Reset the tracking of consumed offsets before entering in priority mode. */
+ private fun resetOffsetTracker() {
+ offsetScrolledBeforePriorityMode = 0f
+ }
+
+ private suspend fun stop(velocityAvailable: Float): Float {
+ check(isPriorityMode) { "This should never happen, stop() was called before start()" }
isPriorityMode = false
+ resetOffsetTracker()
- return onStop(velocity)
+ return coroutineScope {
+ onStopJob = async { onStop(velocityAvailable) }
+ onStopJob.await()
+ }
+ }
+
+ private fun cancel() {
+ check(isPriorityMode) { "This should never happen, cancel() was called before start()" }
+ isPriorityMode = false
+ resetOffsetTracker()
+ onCancel()
}
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index ecef6be49df8..a0fed9064181 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -39,7 +39,6 @@ import com.android.compose.animation.scene.TestScenes.SceneC
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.Transition
import com.android.compose.animation.scene.subjects.assertThat
-import com.android.compose.nestedscroll.SuspendedValue
import com.android.compose.test.MonotonicClockTestScope
import com.android.compose.test.runMonotonicClockTest
import com.android.compose.test.transition
@@ -128,7 +127,7 @@ class DraggableHandlerTest {
val horizontalDraggableHandler = layoutImpl.draggableHandler(Orientation.Horizontal)
var pointerInfoOwner: () -> PointersInfo = {
- PointersInfo(startedPosition = Offset.Zero, pointersDown = 1)
+ PointersInfo(startedPosition = Offset.Zero, pointersDown = 1, isMouseWheel = false)
}
fun nestedScrollConnection(
@@ -1188,7 +1187,9 @@ class DraggableHandlerTest {
val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways)
// Drag from the **top** of the screen
- pointerInfoOwner = { PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1) }
+ pointerInfoOwner = {
+ PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1, isMouseWheel = false)
+ }
assertIdle(currentScene = SceneC)
nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f))
@@ -1206,7 +1207,11 @@ class DraggableHandlerTest {
// Drag from the **bottom** of the screen
pointerInfoOwner = {
- PointersInfo(startedPosition = Offset(0f, SCREEN_SIZE), pointersDown = 1)
+ PointersInfo(
+ startedPosition = Offset(0f, SCREEN_SIZE),
+ pointersDown = 1,
+ isMouseWheel = false,
+ )
}
assertIdle(currentScene = SceneC)
@@ -1221,6 +1226,22 @@ class DraggableHandlerTest {
}
@Test
+ fun ignoreMouseWheel() = runGestureTest {
+ // Start at scene C.
+ navigateToSceneC()
+ val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways)
+
+ // Use mouse wheel
+ pointerInfoOwner = {
+ PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1, isMouseWheel = true)
+ }
+ assertIdle(currentScene = SceneC)
+
+ nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f))
+ assertIdle(currentScene = SceneC)
+ }
+
+ @Test
fun transitionIsImmediatelyUpdatedWhenReleasingFinger() = runGestureTest {
// Swipe up from the middle to transition to scene B.
val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementMatcherTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementMatcherTest.kt
new file mode 100644
index 000000000000..af0962361fb2
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementMatcherTest.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.compose.animation.scene
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestElements.Bar
+import com.android.compose.animation.scene.TestElements.Foo
+import com.android.compose.animation.scene.TestScenes.SceneA
+import com.android.compose.animation.scene.TestScenes.SceneB
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ElementMatcherTest {
+ @Test
+ fun and() {
+ val matcher = Foo and inContent(SceneA)
+ assertThat(matcher.matches(Foo, SceneA)).isTrue()
+ assertThat(matcher.matches(Foo, SceneB)).isFalse()
+ assertThat(matcher.matches(Bar, SceneA)).isFalse()
+ assertThat(matcher.matches(Bar, SceneB)).isFalse()
+ }
+
+ @Test
+ fun or() {
+ val matcher = Foo or inContent(SceneA)
+ assertThat(matcher.matches(Foo, SceneA)).isTrue()
+ assertThat(matcher.matches(Foo, SceneB)).isTrue()
+ assertThat(matcher.matches(Bar, SceneA)).isTrue()
+ assertThat(matcher.matches(Bar, SceneB)).isFalse()
+ }
+
+ @Test
+ fun not() {
+ val matcher = !Foo
+ assertThat(matcher.matches(Foo, SceneA)).isFalse()
+ assertThat(matcher.matches(Foo, SceneB)).isFalse()
+ assertThat(matcher.matches(Bar, SceneA)).isTrue()
+ assertThat(matcher.matches(Bar, SceneB)).isTrue()
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 39d46990dc4b..ee807e6a7ede 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -2221,8 +2221,8 @@ class ElementTest {
// In A => B, Foo is not shared and first fades out from A then fades in
// B.
sharedElement(TestElements.Foo, enabled = false)
- fractionRange(end = 0.5f) { fade(TestElements.Foo.inContent(SceneA)) }
- fractionRange(start = 0.5f) { fade(TestElements.Foo.inContent(SceneB)) }
+ fractionRange(end = 0.5f) { fade(TestElements.Foo.inScene(SceneA)) }
+ fractionRange(start = 0.5f) { fade(TestElements.Foo.inScene(SceneB)) }
}
from(SceneB, to = SceneA) {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index c8f6e6d99933..3df608717414 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -46,7 +46,6 @@ import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Velocity
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.modifiers.thenIf
-import com.android.compose.nestedscroll.SuspendedValue
import com.google.common.truth.Truth.assertThat
import kotlin.properties.Delegates
import kotlinx.coroutines.coroutineScope
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
index cae6617cb11b..7ea414d6b8cd 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
@@ -35,6 +35,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertPositionInRootIsEqualTo
@@ -205,7 +206,8 @@ class OverlayTest {
val key = MovableElementKey("MovableBar", contents = setOf(SceneA, OverlayA, OverlayB))
val elementChildTag = "elementChildTag"
- fun elementChild(content: ContentKey) = hasTestTag(elementChildTag) and inContent(content)
+ fun elementChild(content: ContentKey) =
+ hasTestTag(elementChildTag) and SemanticsMatcher.inContent(content)
@Composable
fun ContentScope.MovableBar() {
@@ -773,7 +775,7 @@ class OverlayTest {
// Overscroll on Overlay A.
scope.launch { state.startTransition(transition(SceneA, OverlayA, progress = { 1.5f })) }
rule
- .onNode(hasTestTag(movableElementChildTag) and inContent(SceneA))
+ .onNode(hasTestTag(movableElementChildTag) and SemanticsMatcher.inContent(SceneA))
.assertPositionInRootIsEqualTo(0.dp, 0.dp)
.assertSizeIsEqualTo(100.dp)
.assertIsDisplayed()
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index f3a34884c756..f5bb5ba032c2 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -727,4 +727,45 @@ class SceneTransitionLayoutStateTest {
// The previous job is cancelled and does not infinitely collect the progress.
job.join()
}
+
+ @Test
+ fun replacedTransitionIsRemovedFromFinishedTransitions() = runTest {
+ val state = MutableSceneTransitionLayoutState(SceneA)
+
+ val aToB =
+ transition(
+ SceneA,
+ SceneB,
+ onFreezeAndAnimate = {
+ // Do nothing, so that this transition stays in the transitionStates list and we
+ // can finish() it manually later.
+ },
+ )
+ val replacingAToB = transition(SceneB, SceneC)
+ val replacingBToC = transition(SceneB, SceneC, replacedTransition = replacingAToB)
+
+ // Start A => B.
+ val aToBJob = state.startTransitionImmediately(animationScope = this, aToB)
+
+ // Start B => C and immediately finish it. It will be flagged as finished in
+ // STLState.finishedTransitions given that A => B is not finished yet.
+ val bToCJob = state.startTransitionImmediately(animationScope = this, replacingAToB)
+ replacingAToB.finish()
+ bToCJob.join()
+
+ // Start a new B => C that replaces the previously finished B => C.
+ val replacingBToCJob =
+ state.startTransitionImmediately(animationScope = this, replacingBToC)
+
+ // Finish A => B.
+ aToB.finish()
+ aToBJob.join()
+
+ // Finish the new B => C.
+ replacingBToC.finish()
+ replacingBToCJob.join()
+
+ assertThat(state.transitionState).isIdle()
+ assertThat(state.transitionState).hasCurrentScene(SceneC)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 28d0a473935d..3001505d0e02 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -17,6 +17,8 @@
package com.android.compose.animation.scene
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.rememberScrollableState
+import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
@@ -39,12 +41,14 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.ScrollWheel
import androidx.compose.ui.test.assertPositionInRootIsEqualTo
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performMouseInput
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeRight
import androidx.compose.ui.test.swipeUp
@@ -102,26 +106,22 @@ class SwipeToSceneTest {
modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.debugName),
) {
scene(
- SceneA,
+ key = SceneA,
userActions =
if (swipesEnabled())
- mapOf(
- Swipe.Left to SceneB,
- Swipe.Down to TestScenes.SceneC,
- Swipe.Up to SceneB,
- )
+ mapOf(Swipe.Left to SceneB, Swipe.Down to SceneC, Swipe.Up to SceneB)
else emptyMap(),
) {
Box(Modifier.fillMaxSize())
}
scene(
- SceneB,
+ key = SceneB,
userActions = if (swipesEnabled()) mapOf(Swipe.Right to SceneA) else emptyMap(),
) {
Box(Modifier.fillMaxSize())
}
scene(
- TestScenes.SceneC,
+ key = SceneC,
userActions =
if (swipesEnabled())
mapOf(
@@ -196,7 +196,7 @@ class SwipeToSceneTest {
// Drag is in progress, so currentScene = SceneA and progress = 56dp / LayoutHeight
transition = assertThat(layoutState.transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
- assertThat(transition).hasToScene(TestScenes.SceneC)
+ assertThat(transition).hasToScene(SceneC)
assertThat(transition).hasCurrentScene(SceneA)
assertThat(transition).hasProgress(56.dp / LayoutHeight)
assertThat(transition).isInitiatedByUserInput()
@@ -206,15 +206,15 @@ class SwipeToSceneTest {
rule.onRoot().performTouchInput { up() }
transition = assertThat(layoutState.transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
- assertThat(transition).hasToScene(TestScenes.SceneC)
- assertThat(transition).hasCurrentScene(TestScenes.SceneC)
+ assertThat(transition).hasToScene(SceneC)
+ assertThat(transition).hasCurrentScene(SceneC)
assertThat(transition).hasProgress(56.dp / LayoutHeight)
assertThat(transition).isInitiatedByUserInput()
// Wait for the animation to finish. We should now be in scene C.
rule.waitForIdle()
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
}
@Test
@@ -271,20 +271,20 @@ class SwipeToSceneTest {
// We should be animating to C (currentScene = SceneC).
transition = assertThat(layoutState.transitionState).isSceneTransition()
assertThat(transition).hasFromScene(SceneA)
- assertThat(transition).hasToScene(TestScenes.SceneC)
- assertThat(transition).hasCurrentScene(TestScenes.SceneC)
+ assertThat(transition).hasToScene(SceneC)
+ assertThat(transition).hasCurrentScene(SceneC)
assertThat(transition).hasProgress(55.dp / LayoutHeight)
// Wait for the animation to finish. We should now be in scene C.
rule.waitForIdle()
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
}
@Test
fun multiPointerSwipe() {
// Start at scene C.
- val layoutState = layoutState(TestScenes.SceneC)
+ val layoutState = layoutState(SceneC)
// The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
// detected as a drag event.
@@ -295,7 +295,7 @@ class SwipeToSceneTest {
}
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
// Swipe down with two fingers.
rule.onRoot().performTouchInput {
@@ -307,7 +307,7 @@ class SwipeToSceneTest {
// We are transitioning to B because we used 2 fingers.
val transition = assertThat(layoutState.transitionState).isSceneTransition()
- assertThat(transition).hasFromScene(TestScenes.SceneC)
+ assertThat(transition).hasFromScene(SceneC)
assertThat(transition).hasToScene(SceneB)
// Release the fingers and wait for the animation to end. We are back to C because we only
@@ -315,13 +315,13 @@ class SwipeToSceneTest {
rule.onRoot().performTouchInput { repeat(2) { i -> up(pointerId = i) } }
rule.waitForIdle()
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
}
@Test
fun defaultEdgeSwipe() {
// Start at scene C.
- val layoutState = layoutState(TestScenes.SceneC)
+ val layoutState = layoutState(SceneC)
// The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
// detected as a drag event.
@@ -332,7 +332,7 @@ class SwipeToSceneTest {
}
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
// Swipe down from the top edge.
rule.onRoot().performTouchInput {
@@ -342,7 +342,7 @@ class SwipeToSceneTest {
// We are transitioning to B (and not A) because we started from the top edge.
var transition = assertThat(layoutState.transitionState).isSceneTransition()
- assertThat(transition).hasFromScene(TestScenes.SceneC)
+ assertThat(transition).hasFromScene(SceneC)
assertThat(transition).hasToScene(SceneB)
// Release the fingers and wait for the animation to end. We are back to C because we only
@@ -350,7 +350,7 @@ class SwipeToSceneTest {
rule.onRoot().performTouchInput { up() }
rule.waitForIdle()
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
// Swipe right from the left edge.
rule.onRoot().performTouchInput {
@@ -360,7 +360,7 @@ class SwipeToSceneTest {
// We are transitioning to B (and not A) because we started from the left edge.
transition = assertThat(layoutState.transitionState).isSceneTransition()
- assertThat(transition).hasFromScene(TestScenes.SceneC)
+ assertThat(transition).hasFromScene(SceneC)
assertThat(transition).hasToScene(SceneB)
// Release the fingers and wait for the animation to end. We are back to C because we only
@@ -368,7 +368,7 @@ class SwipeToSceneTest {
rule.onRoot().performTouchInput { up() }
rule.waitForIdle()
assertThat(layoutState.transitionState).isIdle()
- assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC)
+ assertThat(layoutState.transitionState).hasCurrentScene(SceneC)
}
@Test
@@ -434,7 +434,7 @@ class SwipeToSceneTest {
// We should still correctly compute that we are swiping down to scene C.
var transition = assertThat(layoutState.transitionState).isSceneTransition()
- assertThat(transition).hasToScene(TestScenes.SceneC)
+ assertThat(transition).hasToScene(SceneC)
// Release the finger, animating back to scene A.
rule.onRoot().performTouchInput { up() }
@@ -502,6 +502,89 @@ class SwipeToSceneTest {
}
@Test
+ fun mouseWheel_pointerInputApi_ignoredByStl() {
+ val layoutState = layoutState()
+ var touchSlop = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ TestContent(layoutState)
+ }
+
+ rule.onRoot().performMouseInput {
+ enter(middle)
+ scroll(touchSlop, ScrollWheel.Vertical)
+ }
+
+ // Mouse wheel scroll are ignored
+ assertThat(layoutState.currentTransition).isNull()
+ }
+
+ @Test
+ fun mouseWheel_scrollableCannotScroll_ignoredByStl() {
+ val layoutState = layoutState()
+ var touchSlop = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(layoutState, Modifier.size(LayoutWidth, LayoutHeight)) {
+ scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Box(
+ Modifier.fillMaxSize()
+ // A scrollable that does not consume the scroll gesture
+ .scrollable(rememberScrollableState { 0f }, Orientation.Vertical)
+ )
+ }
+ scene(SceneB) { Box(Modifier.fillMaxSize()) }
+ }
+ }
+
+ rule.onRoot().performMouseInput {
+ enter(middle)
+ scroll(touchSlop, ScrollWheel.Vertical)
+ }
+
+ // Mouse wheel scroll are ignored
+ assertThat(layoutState.currentTransition).isNull()
+ }
+
+ @Test
+ fun mouseWheel_scrollableConsume_ignoredByStl() {
+ val layoutState = layoutState()
+ var touchSlop = 0f
+ var lastScroll = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(layoutState, Modifier.size(LayoutWidth, LayoutHeight)) {
+ scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Box(
+ Modifier.fillMaxSize()
+ // A scrollable that consumes the scroll gesture
+ .scrollable(
+ rememberScrollableState {
+ lastScroll = it
+ it
+ },
+ Orientation.Vertical,
+ )
+ )
+ }
+ scene(SceneB) { Box(Modifier.fillMaxSize()) }
+ }
+ }
+
+ rule.onRoot().performMouseInput {
+ enter(middle)
+ scroll(touchSlop * 10, ScrollWheel.Vertical)
+ }
+
+ // Mouse wheel scroll are ignored
+ assertThat(layoutState.currentTransition).isNull()
+
+ // Mouse wheel scroll can still be consumed by the scrollable
+ assertThat(lastScroll).isNotEqualTo(0f)
+ assertThat(touchSlop).isNotEqualTo(0f)
+ }
+
+ @Test
fun transitionKey() {
val transitionkey = TransitionKey(debugName = "foo")
val state =
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
index 4877cd610875..2e3a934c2701 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/SharedElementTest.kt
@@ -31,7 +31,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TestElements
import com.android.compose.animation.scene.TestScenes
-import com.android.compose.animation.scene.inContent
+import com.android.compose.animation.scene.inScene
import com.android.compose.animation.scene.testTransition
import com.android.compose.test.assertSizeIsEqualTo
import org.junit.Rule
@@ -125,10 +125,10 @@ class SharedElementTest {
sharedElement(TestElements.Foo, enabled = false)
// In SceneA, Foo leaves to the left edge.
- translate(TestElements.Foo.inContent(TestScenes.SceneA), Edge.Left)
+ translate(TestElements.Foo.inScene(TestScenes.SceneA), Edge.Left)
// In SceneB, Foo comes from the bottom edge.
- translate(TestElements.Foo.inContent(TestScenes.SceneB), Edge.Bottom)
+ translate(TestElements.Foo.inScene(TestScenes.SceneB), Edge.Bottom)
},
) {
before { onElement(TestElements.Foo).assertPositionInRootIsEqualTo(10.dp, 50.dp) }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index badc43bd3e0f..1a3b86b936df 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -34,30 +34,31 @@ class PriorityNestedScrollConnectionTest {
private var canStartPreScroll = false
private var canStartPostScroll = false
private var canStartPostFling = false
- private var canContinueScroll = false
+ private var canStopOnPreFling = true
private var isStarted = false
private var lastScroll: Float? = null
- private var returnOnScroll = 0f
+ private var consumeScroll = true
private var lastStop: Float? = null
- private var returnOnStop = 0f
+ private var isCancelled: Boolean = false
+ private var consumeStop = true
private val scrollConnection =
PriorityNestedScrollConnection(
orientation = Orientation.Vertical,
- canStartPreScroll = { _, _ -> canStartPreScroll },
- canStartPostScroll = { _, _ -> canStartPostScroll },
+ canStartPreScroll = { _, _, _ -> canStartPreScroll },
+ canStartPostScroll = { _, _, _ -> canStartPostScroll },
canStartPostFling = { canStartPostFling },
- canContinueScroll = { canContinueScroll },
- canScrollOnFling = false,
+ canStopOnPreFling = { canStopOnPreFling },
onStart = { isStarted = true },
- onScroll = {
- lastScroll = it
- returnOnScroll
+ onScroll = { offsetAvailable, _ ->
+ lastScroll = offsetAvailable
+ if (consumeScroll) offsetAvailable else 0f
},
onStop = {
lastStop = it
- { returnOnStop }
+ if (consumeStop) it else 0f
},
+ onCancel = { isCancelled = true },
)
@Test
@@ -85,7 +86,7 @@ class PriorityNestedScrollConnectionTest {
canStartPostScroll = true
scrollConnection.onPostScroll(
consumed = Offset.Zero,
- available = Offset.Zero,
+ available = Offset(1f, 1f),
source = UserInput,
)
}
@@ -136,45 +137,55 @@ class PriorityNestedScrollConnectionTest {
@Test
fun step2_onPriorityMode_shouldContinueIfAllowed() {
startPriorityModePostScroll()
- canContinueScroll = true
- scrollConnection.onPreScroll(available = Offset(1f, 1f), source = UserInput)
+ val scroll1 = scrollConnection.onPreScroll(available = Offset(0f, 1f), source = UserInput)
assertThat(lastScroll).isEqualTo(1f)
+ assertThat(scroll1.y).isEqualTo(1f)
- canContinueScroll = false
- scrollConnection.onPreScroll(available = Offset(2f, 2f), source = UserInput)
- assertThat(lastScroll).isNotEqualTo(2f)
- assertThat(lastScroll).isEqualTo(1f)
+ consumeScroll = false
+ val scroll2 = scrollConnection.onPreScroll(available = Offset(0f, 2f), source = UserInput)
+ assertThat(lastScroll).isEqualTo(2f)
+ assertThat(scroll2.y).isEqualTo(0f)
}
@Test
- fun step3a_onPriorityMode_shouldStopIfCannotContinue() {
+ fun step3a_onPriorityMode_shouldCancelIfCannotContinue() {
startPriorityModePostScroll()
- canContinueScroll = false
+ consumeScroll = false
- scrollConnection.onPreScroll(available = Offset.Zero, source = UserInput)
+ scrollConnection.onPreScroll(available = Offset(0f, 1f), source = UserInput)
- assertThat(lastStop).isNotNull()
+ assertThat(isCancelled).isTrue()
}
@Test
fun step3b_onPriorityMode_shouldStopOnFling() = runTest {
startPriorityModePostScroll()
- canContinueScroll = true
scrollConnection.onPreFling(available = Velocity.Zero)
- assertThat(lastStop).isNotNull()
+ assertThat(lastStop).isEqualTo(0f)
+ }
+
+ @Test
+ fun ifCannotStopOnPreFling_shouldStopOnPostFling() = runTest {
+ startPriorityModePostScroll()
+ canStopOnPreFling = false
+
+ scrollConnection.onPreFling(available = Velocity.Zero)
+ assertThat(lastStop).isNull()
+
+ scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero)
+ assertThat(lastStop).isEqualTo(0f)
}
@Test
- fun step3c_onPriorityMode_shouldStopOnReset() {
+ fun step3c_onPriorityMode_shouldCancelOnReset() {
startPriorityModePostScroll()
- canContinueScroll = true
scrollConnection.reset()
- assertThat(lastStop).isNotNull()
+ assertThat(isCancelled).isTrue()
}
@Test
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
index 22450d32ea62..b3201d0daffe 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
@@ -25,11 +25,11 @@ fun isElement(element: ElementKey, content: ContentKey? = null): SemanticsMatche
return if (content == null) {
hasTestTag(element.testTag)
} else {
- hasTestTag(element.testTag) and inContent(content)
+ hasTestTag(element.testTag) and SemanticsMatcher.inContent(content)
}
}
/** A [SemanticsMatcher] that matches anything inside [content]. */
-fun inContent(content: ContentKey): SemanticsMatcher {
+fun SemanticsMatcher.Companion.inContent(content: ContentKey): SemanticsMatcher {
return hasAnyAncestor(hasTestTag(content.testTag))
}
diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md
index 234c7a032d2e..bf15b4feadfb 100644
--- a/packages/SystemUI/docs/scene.md
+++ b/packages/SystemUI/docs/scene.md
@@ -63,7 +63,7 @@ the instructions below to turn it on.
NOTE: in case these instructions become stale and don't actually enable the
framework, please make sure `SceneContainerFlag.isEnabled` in the
[`SceneContainerFlag.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt)
-file evalutes to `true`.
+file evaluates to `true`.
1. Set a collection of **aconfig flags** to `true` by running the following
commands:
@@ -73,7 +73,6 @@ file evalutes to `true`.
$ adb shell device_config override systemui com.android.systemui.migrate_clocks_to_blueprint true
$ adb shell device_config override systemui com.android.systemui.notification_avalanche_throttle_hun true
$ adb shell device_config override systemui com.android.systemui.predictive_back_sysui true
- $ adb shell device_config override systemui com.android.systemui.device_entry_udfps_refactor true
$ adb shell device_config override systemui com.android.systemui.scene_container true
```
2. **Restart** System UI by issuing the following command:
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 662815ee7cbe..fcb433b5db4e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -46,7 +46,9 @@ import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import android.testing.TestableLooper;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.LinearLayout;
+import android.widget.Space;
import android.widget.Spinner;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -226,7 +228,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
mDialog.show();
LinearLayout relatedToolsView = (LinearLayout) getRelatedToolsView(mDialog);
- assertThat(relatedToolsView.getChildCount()).isEqualTo(1);
+ assertThat(countChildWithoutSpace(relatedToolsView)).isEqualTo(1);
}
@Test
@@ -244,12 +246,13 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
when(mActivityInfo.loadLabel(mPackageManager)).thenReturn(TEST_LABEL);
when(mActivityInfo.loadIcon(mPackageManager)).thenReturn(mDrawable);
when(mActivityInfo.getComponentName()).thenReturn(TEST_COMPONENT);
+ when(mDrawable.mutate()).thenReturn(mDrawable);
setUpPairNewDeviceDialog();
mDialog.show();
LinearLayout relatedToolsView = (LinearLayout) getRelatedToolsView(mDialog);
- assertThat(relatedToolsView.getChildCount()).isEqualTo(2);
+ assertThat(countChildWithoutSpace(relatedToolsView)).isEqualTo(2);
}
@Test
@@ -364,6 +367,16 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
return dialog.requireViewById(R.id.preset_spinner);
}
+ private int countChildWithoutSpace(ViewGroup viewGroup) {
+ int spaceCount = 0;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ if (viewGroup.getChildAt(i) instanceof Space) {
+ spaceCount++;
+ }
+ }
+ return viewGroup.getChildCount() - spaceCount;
+ }
+
@After
public void reset() {
if (mDialogDelegate != null) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
index 17ce1ddee87a..77369f7ec881 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
@@ -85,7 +85,7 @@ public class HearingDevicesToolItemParserTest extends SysuiTestCase {
}
@Test
- public void parseStringArray_noString_emptyResult() {
+ public void parseStringArray_noToolName_emptyResult() {
assertThat(HearingDevicesToolItemParser.parseStringArray(mContext, new String[]{},
new String[]{})).isEqualTo(emptyList());
}
@@ -103,14 +103,14 @@ public class HearingDevicesToolItemParserTest extends SysuiTestCase {
}
@Test
- public void parseStringArray_fourToolName_maxThreeToolItem() {
+ public void parseStringArray_threeToolNames_maxTwoToolItems() {
String componentNameString = TEST_PKG + "/" + TEST_CLS;
- String[] fourToolName =
- new String[]{componentNameString, componentNameString, componentNameString,
- componentNameString};
+ String[] threeToolNames =
+ new String[]{componentNameString, componentNameString, componentNameString};
List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
- fourToolName, new String[]{});
+ threeToolNames, new String[]{});
+
assertThat(toolItemList.size()).isEqualTo(HearingDevicesToolItemParser.MAX_NUM);
}
@@ -120,6 +120,7 @@ public class HearingDevicesToolItemParserTest extends SysuiTestCase {
List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
wrongFormatToolName, new String[]{});
+
assertThat(toolItemList.size()).isEqualTo(0);
}
@@ -129,6 +130,7 @@ public class HearingDevicesToolItemParserTest extends SysuiTestCase {
List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
notExistToolName, new String[]{});
+
assertThat(toolItemList.size()).isEqualTo(0);
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
deleted file mode 100644
index 13306becf6d2..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import org.junit.Assert.assertFalse
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.junit.MockitoJUnit
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
-class UdfpsBpViewControllerTest : SysuiTestCase() {
-
- @JvmField @Rule var rule = MockitoJUnit.rule()
-
- @Mock lateinit var udfpsBpView: UdfpsBpView
- @Mock lateinit var statusBarStateController: StatusBarStateController
- @Mock lateinit var shadeInteractor: ShadeInteractor
- @Mock lateinit var systemUIDialogManager: SystemUIDialogManager
- @Mock lateinit var dumpManager: DumpManager
- @Mock lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
-
- private lateinit var udfpsBpViewController: UdfpsBpViewController
-
- @Before
- fun setup() {
- udfpsBpViewController =
- UdfpsBpViewController(
- udfpsBpView,
- statusBarStateController,
- shadeInteractor,
- systemUIDialogManager,
- dumpManager,
- udfpsOverlayInteractor,
- )
- }
-
- @TestableLooper.RunWithLooper(setAsMainLooper = true)
- @Test
- fun testShouldNeverPauseAuth() {
- assertFalse(udfpsBpViewController.shouldPauseAuth())
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 437a4b357372..21c6583d4e84 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -16,34 +16,17 @@
package com.android.systemui.biometrics;
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
-import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_UP;
-
-import static com.android.internal.util.FunctionalUtils.ThrowingConsumer;
-import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.graphics.Rect;
-import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricRequestConstants;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
@@ -58,13 +41,9 @@ import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
import android.testing.TestableLooper.RunWithLooper;
-import android.util.Pair;
-import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
-import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.accessibility.AccessibilityManager;
@@ -75,15 +54,11 @@ import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
-import com.android.systemui.biometrics.udfps.InteractionEvent;
-import com.android.systemui.biometrics.udfps.NormalizedTouchData;
import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
-import com.android.systemui.biometrics.udfps.TouchProcessorResult;
import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel;
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -102,7 +77,6 @@ import com.android.systemui.power.data.repository.FakePowerRepository;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.power.shared.model.WakeSleepReason;
import com.android.systemui.power.shared.model.WakefulnessState;
-import com.android.systemui.res.R;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
@@ -130,7 +104,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
-import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -204,16 +177,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
private FeatureFlags mFeatureFlags;
// Stuff for configuring mocks
@Mock
- private UdfpsView mUdfpsView;
- @Mock
- private UdfpsBpView mBpView;
- @Mock
- private UdfpsFpmEmptyView mFpmEmptyView;
- @Mock
- private UdfpsKeyguardViewLegacy mKeyguardView;
- private final UdfpsAnimationViewController mUdfpsKeyguardViewController =
- mock(UdfpsKeyguardViewControllerLegacy.class);
- @Mock
private SystemUIDialogManager mSystemUIDialogManager;
@Mock
private ActivityTransitionAnimator mActivityTransitionAnimator;
@@ -241,8 +204,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<View> mViewCaptor;
@Captor
- private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor;
- @Captor
private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor;
@Captor
private ArgumentCaptor<Runnable> mOnDisplayConfiguredCaptor;
@@ -287,18 +248,9 @@ public class UdfpsControllerTest extends SysuiTestCase {
mContext.getOrCreateTestableResources()
.addOverride(com.android.internal.R.bool.config_ignoreUdfpsVote, false);
- when(mLayoutInflater.inflate(R.layout.udfps_view, null, false))
- .thenReturn(mUdfpsView);
- when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view_legacy, null))
- .thenReturn(mKeyguardView); // for showOverlay REASON_AUTH_FPM_KEYGUARD
- when(mLayoutInflater.inflate(R.layout.udfps_bp_view, null))
- .thenReturn(mBpView);
- when(mLayoutInflater.inflate(R.layout.udfps_fpm_empty_view, null))
- .thenReturn(mFpmEmptyView);
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
when(mSessionTracker.getSessionId(anyInt())).thenReturn(
(new InstanceIdSequence(1 << 20)).newInstanceId());
- when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
@@ -327,7 +279,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
// Create a fake background executor.
mBiometricExecutor = new FakeExecutor(new FakeSystemClock());
- mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
initUdfpsController(mOpticalProps);
}
@@ -349,7 +300,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
mFalsingManager,
mPowerManager,
mAccessibilityManager,
- mLockscreenShadeTransitionController,
mScreenLifecycle,
mVibrator,
mUdfpsHapticsSimulator,
@@ -390,101 +340,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
}
@Test
- public void dozeTimeTick() throws RemoteException {
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
- mUdfpsController.dozeTimeTick();
- verify(mUdfpsView).dozeTimeTick();
- }
-
- @Test
- public void onActionDownTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException {
- // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
- when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
- when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
-
- final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
- // WHEN ACTION_DOWN is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.first);
- MotionEvent downEvent = obtainMotionEvent(ACTION_DOWN, 0, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- // THEN notify keyguard authenticate to dismiss the keyguard
- verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
- }
-
- @Test
- public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException {
- onActionMoveTouch_whenCanDismissLockScreen_entersDevice(false /* stale */);
- }
-
- @Test
- public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice_ignoreStale()
- throws RemoteException {
- onActionMoveTouch_whenCanDismissLockScreen_entersDevice(true /* stale */);
- }
-
- public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice(boolean stale)
- throws RemoteException {
- // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
- when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
- when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
-
- final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
- // WHEN ACTION_MOVE is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.first);
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- if (stale) {
- mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId);
- mFgExecutor.runAllReady();
- }
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
-
- // THEN notify keyguard authenticate to dismiss the keyguard
- verify(mStatusBarKeyguardViewManager, stale ? never() : times(1))
- .notifyKeyguardAuthenticated(anyBoolean());
- }
-
- @Test
- public void onMultipleTouch_whenCanDismissLockScreen_entersDeviceOnce() throws RemoteException {
- // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
- when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
- when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
-
- final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false);
-
- // GIVEN that the overlay is showing
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.first);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.second);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
-
- // THEN notify keyguard authenticate to dismiss the keyguard
- verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean());
- }
-
- @Test
public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException {
// GIVEN overlay was showing and the udfps bouncer is showing
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
@@ -524,61 +379,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
}
@Test
- public void updateOverlayParams_recreatesOverlay_ifParamsChanged() throws Exception {
- final Rect[] sensorBounds = new Rect[]{new Rect(10, 10, 20, 20), new Rect(5, 5, 25, 25)};
- final int[] displayWidth = new int[]{1080, 1440};
- final int[] displayHeight = new int[]{1920, 2560};
- final float[] scaleFactor = new float[]{1f, displayHeight[1] / (float) displayHeight[0]};
- final int[] rotation = new int[]{Surface.ROTATION_0, Surface.ROTATION_90};
- final UdfpsOverlayParams oldParams = new UdfpsOverlayParams(sensorBounds[0],
- sensorBounds[0], displayWidth[0], displayHeight[0], scaleFactor[0], rotation[0],
- FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
-
- for (int i1 = 0; i1 <= 1; ++i1) {
- for (int i2 = 0; i2 <= 1; ++i2) {
- for (int i3 = 0; i3 <= 1; ++i3) {
- for (int i4 = 0; i4 <= 1; ++i4) {
- for (int i5 = 0; i5 <= 1; ++i5) {
- final UdfpsOverlayParams newParams = new UdfpsOverlayParams(
- sensorBounds[i1], sensorBounds[i1], displayWidth[i2],
- displayHeight[i3], scaleFactor[i4], rotation[i5],
- FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
-
- if (newParams.equals(oldParams)) {
- continue;
- }
-
- // Initialize the overlay with old parameters.
- mUdfpsController.updateOverlayParams(mOpticalProps, oldParams);
-
- // Show the overlay.
- reset(mWindowManager);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID,
- mOpticalProps.sensorId,
- BiometricRequestConstants.REASON_ENROLL_ENROLLING,
- mUdfpsOverlayControllerCallback);
-
- mFgExecutor.runAllReady();
- verify(mWindowManager).addView(mViewCaptor.capture(), any());
- when(mViewCaptor.getValue().getParent())
- .thenReturn(mock(ViewGroup.class));
-
- // Update overlay parameters.
- reset(mWindowManager);
- mUdfpsController.updateOverlayParams(mOpticalProps, newParams);
- mFgExecutor.runAllReady();
-
- // Ensure the overlay was recreated.
- verify(mWindowManager).removeView(any());
- verify(mWindowManager).addView(any(), any());
- }
- }
- }
- }
- }
- }
-
- @Test
public void updateOverlayParams_doesNothing_ifParamsDidntChange() throws Exception {
final Rect sensorBounds = new Rect(10, 10, 20, 20);
final int displayWidth = 1080;
@@ -595,7 +395,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
- verify(mWindowManager).addView(any(), any());
// Update overlay with the same parameters.
mUdfpsController.updateOverlayParams(mOpticalProps,
@@ -606,870 +405,4 @@ public class UdfpsControllerTest extends SysuiTestCase {
// Ensure the overlay was not recreated.
verify(mWindowManager, never()).removeView(any());
}
-
- private static MotionEvent obtainMotionEvent(int action, float x, float y, float minor,
- float major) {
- MotionEvent.PointerProperties pp = new MotionEvent.PointerProperties();
- pp.id = 1;
- MotionEvent.PointerCoords pc = new MotionEvent.PointerCoords();
- pc.x = x;
- pc.y = y;
- pc.touchMinor = minor;
- pc.touchMajor = major;
- return MotionEvent.obtain(0, 0, action, 1, new MotionEvent.PointerProperties[]{pp},
- new MotionEvent.PointerCoords[]{pc}, 0, 0, 1f, 1f, 0, 0, 0, 0);
- }
-
- private static class TestParams {
- public final FingerprintSensorPropertiesInternal sensorProps;
-
- TestParams(FingerprintSensorPropertiesInternal sensorProps) {
- this.sensorProps = sensorProps;
- }
- }
-
- private void runWithAllParams(ThrowingConsumer<TestParams> testParamsConsumer) {
- for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps,
- mUltrasonicProps)) {
- initUdfpsController(sensorProps);
- testParamsConsumer.accept(new TestParams(sensorProps));
- }
- }
-
- @Test
- public void onTouch_propagatesTouchInNativeOrientationAndResolution() {
- runWithAllParams(
- this::onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized);
- }
-
- private void onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized(
- TestParams testParams) throws RemoteException {
- reset(mUdfpsView);
- when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
- final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner.
- final int pointerId = 0;
- final int displayWidth = 1080;
- final int displayHeight = 1920;
- final float scaleFactor = 1f; // This means the native resolution is 1440x2560.
- final float touchMinor = 10f;
- final float touchMajor = 20f;
- final float orientation = 30f;
-
- // Expecting a touch at the very bottom right corner in native orientation and resolution.
- final float expectedX = displayWidth / scaleFactor;
- final float expectedY = displayHeight / scaleFactor;
- final float expectedMinor = touchMinor / scaleFactor;
- final float expectedMajor = touchMajor / scaleFactor;
-
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-
- // GIVEN a valid touch on sensor
- NormalizedTouchData touchData = new NormalizedTouchData(pointerId, displayWidth,
- displayHeight, touchMinor, touchMajor, orientation, 0L, 0L);
- TouchProcessorResult processorDownResult = new TouchProcessorResult.ProcessedTouch(
- InteractionEvent.DOWN, 1, touchData);
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorDownResult);
-
- // Show the overlay.
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
- // Test ROTATION_0
- mUdfpsController.updateOverlayParams(testParams.sensorProps,
- new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
- scaleFactor, Surface.ROTATION_0,
- FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
- MotionEvent event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor,
- touchMajor);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
- event.recycle();
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
- eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
- anyBoolean());
-
- // Test ROTATION_90
- reset(mFingerprintManager);
- mUdfpsController.updateOverlayParams(testParams.sensorProps,
- new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
- scaleFactor, Surface.ROTATION_90,
- FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
- event = obtainMotionEvent(ACTION_DOWN, displayHeight, 0, touchMinor, touchMajor);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
- event.recycle();
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
- eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
- anyBoolean());
-
- // Test ROTATION_270
- reset(mFingerprintManager);
- mUdfpsController.updateOverlayParams(testParams.sensorProps,
- new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
- scaleFactor, Surface.ROTATION_270,
- FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
- event = obtainMotionEvent(ACTION_DOWN, 0, displayWidth, touchMinor, touchMajor);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
- event.recycle();
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
- eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
- anyBoolean());
-
- // Test ROTATION_180
- reset(mFingerprintManager);
- mUdfpsController.updateOverlayParams(testParams.sensorProps,
- new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight,
- scaleFactor, Surface.ROTATION_180,
- FingerprintSensorProperties.TYPE_UDFPS_OPTICAL));
- // ROTATION_180 is not supported. It should be treated like ROTATION_0.
- event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, touchMajor);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
- event.recycle();
- verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY),
- eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(),
- anyBoolean());
- }
-
- @Test
- public void fingerDown() {
- runWithAllParams(this::fingerDownParameterized);
- }
-
- private void fingerDownParameterized(TestParams testParams) throws RemoteException {
- reset(mUdfpsView, mFingerprintManager, mLatencyTracker,
- mKeyguardUpdateMonitor);
- when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
-
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
- InteractionEvent.DOWN, 1 /* pointerId */, touchData);
-
- initUdfpsController(testParams.sensorProps);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- // WHEN ACTION_DOWN is received
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDown);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- // THEN the touch provider is notified about onPointerDown.
- verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
-
- // AND display configuration begins
- if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
- verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
- verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
- } else {
- verify(mLatencyTracker, never()).onActionStart(
- eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
- verify(mUdfpsView, never()).configureDisplay(any());
- }
- verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
-
- if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
- // AND onDisplayConfigured notifies FingerprintManager about onUiReady
- mOnDisplayConfiguredCaptor.getValue().run();
- mBiometricExecutor.runAllReady();
- InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker);
- inOrder.verify(mFingerprintManager).onUdfpsUiEvent(
- eq(FingerprintManager.UDFPS_UI_READY), eq(TEST_REQUEST_ID),
- eq(testParams.sensorProps.sensorId));
- inOrder.verify(mLatencyTracker).onActionEnd(
- eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
- } else {
- verify(mFingerprintManager, never()).onUdfpsUiEvent(
- eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt());
- verify(mLatencyTracker, never()).onActionEnd(
- eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
- }
- }
-
- @Test
- public void aodInterrupt() {
- runWithAllParams(this::aodInterruptParameterized);
- }
-
- private void aodInterruptParameterized(TestParams testParams) throws RemoteException {
- mUdfpsController.cancelAodSendFingerUpAction();
- reset(mUdfpsView, mFingerprintManager, mKeyguardUpdateMonitor);
- when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
- when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
- // GIVEN that the overlay is showing and screen is on and fp is running
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mScreenObserver.onScreenTurnedOn();
- mFgExecutor.runAllReady();
- // WHEN fingerprint is requested because of AOD interrupt
- mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
- mFgExecutor.runAllReady();
- if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
- // THEN display configuration begins
- // AND onDisplayConfigured notifies FingerprintManager about onUiReady
- verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
- mOnDisplayConfiguredCaptor.getValue().run();
- } else {
- verify(mUdfpsView, never()).configureDisplay(mOnDisplayConfiguredCaptor.capture());
- }
- mBiometricExecutor.runAllReady();
-
- verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
- }
-
- @Test
- public void tryAodSendFingerUp_displayConfigurationChanges() {
- runWithAllParams(this::cancelAodInterruptParameterized);
- }
-
- private void cancelAodInterruptParameterized(TestParams testParams) throws RemoteException {
- reset(mUdfpsView);
-
- // GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mScreenObserver.onScreenTurnedOn();
- mFgExecutor.runAllReady();
- mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
- if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
- when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
- // WHEN it is cancelled
- mUdfpsController.tryAodSendFingerUp();
- // THEN the display is unconfigured
- verify(mUdfpsView).unconfigureDisplay();
- } else {
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- // WHEN it is cancelled
- mUdfpsController.tryAodSendFingerUp();
- // THEN the display configuration is unchanged.
- verify(mUdfpsView, never()).unconfigureDisplay();
- }
- }
-
- @Test
- public void onFingerUp_displayConfigurationChange() {
- runWithAllParams(this::onFingerUp_displayConfigurationParameterized);
- }
-
- private void onFingerUp_displayConfigurationParameterized(TestParams testParams)
- throws RemoteException {
- reset(mUdfpsView);
- when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
- final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
- // GIVEN AOD interrupt
- mScreenObserver.onScreenTurnedOn();
- mFgExecutor.runAllReady();
- mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
- if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
- when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
-
- // WHEN up-action received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.second);
- MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
- mBiometricExecutor.runAllReady();
- upEvent.recycle();
- mFgExecutor.runAllReady();
-
- // THEN the display is unconfigured
- verify(mUdfpsView).unconfigureDisplay();
- } else {
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-
- // WHEN up-action received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.second);
- MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
- mBiometricExecutor.runAllReady();
- upEvent.recycle();
- mFgExecutor.runAllReady();
-
- // THEN the display configuration is unchanged.
- verify(mUdfpsView, never()).unconfigureDisplay();
- }
- }
-
- @Test
- public void onAcquiredGood_displayConfigurationChange() {
- runWithAllParams(this::onAcquiredGood_displayConfigurationParameterized);
- }
-
- private void onAcquiredGood_displayConfigurationParameterized(TestParams testParams)
- throws RemoteException {
- reset(mUdfpsView);
-
- // GIVEN overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
- if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
- when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
- // WHEN ACQUIRED_GOOD received
- mOverlayController.onAcquired(testParams.sensorProps.sensorId,
- BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD);
- mFgExecutor.runAllReady();
- // THEN the display is unconfigured
- verify(mUdfpsView).unconfigureDisplay();
- } else {
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- // WHEN ACQUIRED_GOOD received
- mOverlayController.onAcquired(testParams.sensorProps.sensorId,
- BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD);
- mFgExecutor.runAllReady();
- // THEN the display configuration is unchanged.
- verify(mUdfpsView, never()).unconfigureDisplay();
- }
- }
-
- @Test
- public void aodInterruptTimeout() {
- runWithAllParams(this::aodInterruptTimeoutParameterized);
- }
-
- private void aodInterruptTimeoutParameterized(TestParams testParams) throws RemoteException {
- reset(mUdfpsView);
-
- // GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mScreenObserver.onScreenTurnedOn();
- mFgExecutor.runAllReady();
- mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
- mFgExecutor.runAllReady();
- if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
- when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
- } else {
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- }
- // WHEN it times out
- mFgExecutor.advanceClockToNext();
- mFgExecutor.runAllReady();
- if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
- // THEN the display is unconfigured.
- verify(mUdfpsView).unconfigureDisplay();
- } else {
- // THEN the display configuration is unchanged.
- verify(mUdfpsView, never()).unconfigureDisplay();
- }
- }
-
- @Test
- public void aodInterruptCancelTimeoutActionOnFingerUp() {
- runWithAllParams(this::aodInterruptCancelTimeoutActionOnFingerUpParameterized);
- }
-
- private void aodInterruptCancelTimeoutActionOnFingerUpParameterized(TestParams testParams)
- throws RemoteException {
- reset(mUdfpsView);
- when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl);
-
- // GIVEN AOD interrupt
- mScreenObserver.onScreenTurnedOn();
- mFgExecutor.runAllReady();
- mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
- mFgExecutor.runAllReady();
-
- final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
-
- if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
- // Configure UdfpsView to accept the ACTION_UP event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
- } else {
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- }
-
- // WHEN ACTION_UP is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.second);
- MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
- mBiometricExecutor.runAllReady();
- upEvent.recycle();
-
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-
- // WHEN ACTION_DOWN is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.first);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- mFgExecutor.runAllReady();
-
- if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
- // Configure UdfpsView to accept the finger up event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
- } else {
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
- }
-
- // WHEN it times out
- mFgExecutor.advanceClockToNext();
- mFgExecutor.runAllReady();
-
- if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) {
- // THEN the display should be unconfigured once. If the timeout action is not
- // cancelled, the display would be unconfigured twice which would cause two
- // FP attempts.
- verify(mUdfpsView).unconfigureDisplay();
- } else {
- verify(mUdfpsView, never()).unconfigureDisplay();
- }
- }
-
- @Test
- public void aodInterruptScreenOff() {
- runWithAllParams(this::aodInterruptScreenOffParameterized);
- }
-
- private void aodInterruptScreenOffParameterized(TestParams testParams) throws RemoteException {
- reset(mUdfpsView);
-
- // GIVEN screen off
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mScreenObserver.onScreenTurnedOff();
- mFgExecutor.runAllReady();
-
- // WHEN aod interrupt is received
- mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-
- // THEN display doesn't get configured because it's off
- verify(mUdfpsView, never()).configureDisplay(any());
- }
-
- @Test
- public void aodInterrupt_fingerprintNotRunning() {
- runWithAllParams(this::aodInterrupt_fingerprintNotRunningParameterized);
- }
-
- private void aodInterrupt_fingerprintNotRunningParameterized(TestParams testParams)
- throws RemoteException {
- reset(mUdfpsView);
-
- // GIVEN showing overlay
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mScreenObserver.onScreenTurnedOn();
- mFgExecutor.runAllReady();
-
- // WHEN aod interrupt is received when the fingerprint service isn't running
- when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
- mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
-
- // THEN display doesn't get configured because it's off
- verify(mUdfpsView, never()).configureDisplay(any());
- }
-
- @Test
- public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException {
- final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, true);
-
- // WHEN ACTION_HOVER is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.first);
- verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture());
- MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
- mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent);
- enterEvent.recycle();
-
- // THEN context click haptic is played
- verify(mVibrator).performHapticFeedback(
- any(),
- eq(HapticFeedbackConstants.CONTEXT_CLICK)
- );
- }
-
- @Test
- public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException {
- final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
- // WHEN ACTION_DOWN is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.first);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- // THEN NO haptic played
- verify(mVibrator, never()).performHapticFeedback(any(), anyInt());
- }
-
- @Test
- public void fingerDown_falsingManagerInformed() throws RemoteException {
- final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
- // WHEN ACTION_DOWN is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.first);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- // THEN falsing manager is informed of the touch
- verify(mFalsingManager).isFalseTouch(UDFPS_AUTHENTICATION);
- }
-
- private Pair<TouchProcessorResult, TouchProcessorResult> givenFingerEvent(
- InteractionEvent event1, InteractionEvent event2, boolean a11y)
- throws RemoteException {
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
- event1, 1 /* pointerId */, touchData);
- final TouchProcessorResult processorResultUp = new TouchProcessorResult.ProcessedTouch(
- event2, 1 /* pointerId */, touchData);
-
- // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
- initUdfpsController(mOpticalProps);
-
- // Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
-
- // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(a11y);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- if (a11y) {
- verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture());
- } else {
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
- }
-
- return new Pair<>(processorResultDown, processorResultUp);
- }
-
- @Test
- public void onTouch_forwardToKeyguard() throws RemoteException {
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
- InteractionEvent.UNCHANGED, -1 /* pointerOnSensorId */, touchData);
-
- // GIVEN that the overlay is showing and a11y touch exploration NOT enabled
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
- // WHEN ACTION_DOWN is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDown);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
-
- // THEN the touch is forwarded to Keyguard
- verify(mStatusBarKeyguardViewManager).onTouch(downEvent);
- }
-
- @Test
- public void onTouch_pilferPointer() throws RemoteException {
- final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false);
-
- // WHEN ACTION_DOWN is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.first);
- MotionEvent event = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
-
- // WHEN ACTION_MOVE is received after
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.second);
- event.setAction(ACTION_MOVE);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
- mBiometricExecutor.runAllReady();
- event.recycle();
-
- // THEN only pilfer once on the initial down
- verify(mInputManager).pilferPointers(any());
- }
-
- @Test
- public void onTouch_doNotPilferPointer() throws RemoteException {
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultUnchanged =
- new TouchProcessorResult.ProcessedTouch(InteractionEvent.UNCHANGED,
- -1 /* pointerId */, touchData);
-
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
- // WHEN ACTION_DOWN is received and touch is not within sensor
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultUnchanged);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- // THEN the touch is NOT pilfered
- verify(mInputManager, never()).pilferPointers(any());
- }
-
- @Test
- public void onDownTouchReceivedWithoutPreviousUp() throws RemoteException {
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultDown =
- new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
- -1 /* pointerId */, touchData);
-
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
- // WHEN ACTION_DOWN is received and touch is within sensor
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDown);
- MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent);
- mBiometricExecutor.runAllReady();
- firstDownEvent.recycle();
-
- // And another ACTION_DOWN is received without an ACTION_UP before
- MotionEvent secondDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, secondDownEvent);
- mBiometricExecutor.runAllReady();
- secondDownEvent.recycle();
-
- // THEN the touch is still processed
- verify(mFingerprintManager, times(2)).onPointerDown(anyLong(), anyInt(), anyInt(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
- anyBoolean());
- }
-
- @Test
- public void onAodDownAndDownTouchReceived() throws RemoteException {
- final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
- 0L);
- final TouchProcessorResult processorResultDown =
- new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
- -1 /* pointerId */, touchData);
-
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
-
- // WHEN fingerprint is requested because of AOD interrupt
- // GIVEN there's been an AoD interrupt
- when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
- mScreenObserver.onScreenTurnedOn();
- mUdfpsController.onAodInterrupt(0, 0, 0, 0);
- mFgExecutor.runAllReady();
-
- // and an ACTION_DOWN is received and touch is within sensor
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDown);
- MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent);
- mBiometricExecutor.runAllReady();
- firstDownEvent.recycle();
-
- // THEN the touch is only processed once
- verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
- anyBoolean());
- }
-
- @Test
- public void onTouch_pilferPointerWhenAltBouncerShowing()
- throws RemoteException {
- final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false);
-
- // WHEN alternate bouncer is showing
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
- // WHEN ACTION_DOWN is received and touch is not within sensor
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.first);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- // THEN the touch is pilfered
- verify(mInputManager).pilferPointers(any());
- }
-
- @Test
- public void onTouch_doNotProcessTouchWhenPullingUpBouncer()
- throws RemoteException {
- final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false);
-
- // GIVEN a swipe up to bring up primary bouncer is in progress or swipe down for QS
- when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(true);
- when(mLockscreenShadeTransitionController.getFractionToShade()).thenReturn(1f);
-
- // WHEN ACTION_MOVE is received and touch is within sensor
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.first);
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
-
- // THEN the touch is NOT processed
- verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
- anyBoolean());
- }
-
-
- @Test
- public void onTouch_qsDrag_processesTouchWhenAlternateBouncerVisible()
- throws RemoteException {
- final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
- givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false);
-
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- // GIVEN swipe down for QS
- when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(false);
- when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(1f);
-
- // WHEN ACTION_MOVE is received and touch is within sensor
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- touchProcessorResult.first);
- MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
- mBiometricExecutor.runAllReady();
- moveEvent.recycle();
-
- // THEN the touch is still processed
- verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(),
- anyBoolean());
- }
-
- @Test
- public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException {
- // GIVEN UDFPS overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- // GIVEN there's been an AoD interrupt
- when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
- mScreenObserver.onScreenTurnedOn();
- mUdfpsController.onAodInterrupt(0, 0, 0, 0);
-
- // THEN finger is considered down
- assertTrue(mUdfpsController.isFingerDown());
-
- // WHEN udfps receives an ACQUIRED_GOOD after the display is configured
- when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
- verify(mFingerprintManager).setUdfpsOverlayController(
- mUdfpsOverlayControllerCaptor.capture());
- mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_GOOD);
- mFgExecutor.runAllReady();
-
- // THEN is fingerDown should be FALSE
- assertFalse(mUdfpsController.isFingerDown());
- }
-
- @Test
- public void playHaptic_onAodInterrupt_onAcquiredBad_performHapticFeedback()
- throws RemoteException {
- // GIVEN UDFPS overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- // GIVEN there's been an AoD interrupt
- when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
- mScreenObserver.onScreenTurnedOn();
- mUdfpsController.onAodInterrupt(0, 0, 0, 0);
-
- // THEN vibrate is used
- verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
- }
-
- @Test
- public void onAcquiredCalbacks() {
- runWithAllParams(
- this::ultrasonicCallbackOnAcquired);
- }
-
- public void ultrasonicCallbackOnAcquired(TestParams testParams) throws RemoteException{
- if (testParams.sensorProps.sensorType
- == FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC) {
- reset(mUdfpsView);
-
- UdfpsController.Callback callbackMock = mock(UdfpsController.Callback.class);
- mUdfpsController.addCallback(callbackMock);
-
- // GIVEN UDFPS overlay is showing
- mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId,
- BiometricRequestConstants.REASON_AUTH_KEYGUARD,
- mUdfpsOverlayControllerCallback);
- mFgExecutor.runAllReady();
-
- verify(mFingerprintManager).setUdfpsOverlayController(
- mUdfpsOverlayControllerCaptor.capture());
- mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_START);
- mFgExecutor.runAllReady();
-
- verify(callbackMock).onFingerDown();
-
- mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_GOOD);
- mFgExecutor.runAllReady();
-
- verify(callbackMock).onFingerUp();
- }
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
deleted file mode 100644
index 7986051de3e0..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityTransitionAnimator;
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeExpansionChangeEvent;
-import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.shade.domain.interactor.ShadeInteractor;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
-import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import org.junit.Before;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase {
- // Dependencies
- protected @Mock UdfpsKeyguardViewLegacy mView;
- protected @Mock Context mResourceContext;
- protected @Mock StatusBarStateController mStatusBarStateController;
- protected @Mock ShadeExpansionStateManager mShadeExpansionStateManager;
- protected @Mock StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- protected @Mock LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- protected @Mock DumpManager mDumpManager;
- protected @Mock DelayableExecutor mExecutor;
- protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- protected @Mock KeyguardStateController mKeyguardStateController;
- protected @Mock KeyguardViewMediator mKeyguardViewMediator;
- protected @Mock ConfigurationController mConfigurationController;
- protected @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
- protected @Mock SystemUIDialogManager mDialogManager;
- protected @Mock UdfpsController mUdfpsController;
- protected @Mock ActivityTransitionAnimator mActivityTransitionAnimator;
- protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
- protected @Mock ShadeInteractor mShadeInteractor;
- protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor;
- protected @Mock UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
- protected @Mock SelectedUserInteractor mSelectedUserInteractor;
- protected @Mock KeyguardTransitionInteractor mKeyguardTransitionInteractor;
- protected @Mock UdfpsOverlayInteractor mUdfpsOverlayInteractor;
-
- protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
-
- protected UdfpsKeyguardViewControllerLegacy mController;
-
- // Capture listeners so that they can be used to send events
- private @Captor ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
- protected StatusBarStateController.StateListener mStatusBarStateListener;
-
- private @Captor ArgumentCaptor<KeyguardStateController.Callback>
- mKeyguardStateControllerCallbackCaptor;
- protected KeyguardStateController.Callback mKeyguardStateControllerCallback;
-
- private @Captor ArgumentCaptor<StatusBarKeyguardViewManager.KeyguardViewManagerCallback>
- mKeyguardViewManagerCallbackArgumentCaptor;
- protected StatusBarKeyguardViewManager.KeyguardViewManagerCallback mKeyguardViewManagerCallback;
-
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
- when(mView.getContext()).thenReturn(mResourceContext);
- when(mResourceContext.getString(anyInt())).thenReturn("test string");
- when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false);
- when(mView.getUnpausedAlpha()).thenReturn(255);
- when(mShadeExpansionStateManager.addExpansionListener(any())).thenReturn(
- new ShadeExpansionChangeEvent(0, false, false));
- mController = createUdfpsKeyguardViewController();
- }
-
- protected void sendStatusBarStateChanged(int statusBarState) {
- mStatusBarStateListener.onStateChanged(statusBarState);
- }
-
- protected void captureStatusBarStateListeners() {
- verify(mStatusBarStateController).addCallback(mStateListenerCaptor.capture());
- mStatusBarStateListener = mStateListenerCaptor.getValue();
- }
-
- protected void captureKeyguardStateControllerCallback() {
- verify(mKeyguardStateController).addCallback(
- mKeyguardStateControllerCallbackCaptor.capture());
- mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue();
- }
-
- public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() {
- return createUdfpsKeyguardViewController(false);
- }
-
- public void captureKeyGuardViewManagerCallback() {
- verify(mStatusBarKeyguardViewManager).addCallback(
- mKeyguardViewManagerCallbackArgumentCaptor.capture());
- mKeyguardViewManagerCallback = mKeyguardViewManagerCallbackArgumentCaptor.getValue();
- }
-
- protected UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController(
- boolean useModernBouncer) {
- UdfpsKeyguardViewControllerLegacy controller = new UdfpsKeyguardViewControllerLegacy(
- mView,
- mStatusBarStateController,
- mStatusBarKeyguardViewManager,
- mKeyguardUpdateMonitor,
- mDumpManager,
- mLockscreenShadeTransitionController,
- mConfigurationController,
- mKeyguardStateController,
- mUnlockedScreenOffAnimationController,
- mDialogManager,
- mUdfpsController,
- mActivityTransitionAnimator,
- mPrimaryBouncerInteractor,
- mAlternateBouncerInteractor,
- mUdfpsKeyguardAccessibilityDelegate,
- mSelectedUserInteractor,
- mKeyguardTransitionInteractor,
- mShadeInteractor,
- mUdfpsOverlayInteractor);
- return controller;
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
deleted file mode 100644
index 98d8b054716c..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.TestableLooper;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.statusbar.StatusBarState;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class UdfpsKeyguardViewLegacyControllerTest extends
- UdfpsKeyguardViewLegacyControllerBaseTest {
- @Override
- public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() {
- return createUdfpsKeyguardViewController(/* useModernBouncer */ false);
- }
-
- @Test
- public void testShouldPauseAuth_bouncerShowing() {
- mController.onViewAttached();
- captureStatusBarStateListeners();
- sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
- when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
- when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true);
- when(mView.getUnpausedAlpha()).thenReturn(0);
- assertTrue(mController.shouldPauseAuth());
- }
-
- @Test
- public void testRegistersStatusBarStateListenersOnAttached() {
- mController.onViewAttached();
- captureStatusBarStateListeners();
- }
-
- @Test
- public void testViewControllerQueriesSBStateOnAttached() {
- mController.onViewAttached();
- verify(mStatusBarStateController).getState();
-
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
- captureStatusBarStateListeners();
-
- mController.onViewAttached();
- verify(mView, atLeast(1)).setPauseAuth(true);
- }
-
- @Test
- public void testListenersUnregisteredOnDetached() {
- mController.onViewAttached();
- captureStatusBarStateListeners();
- captureKeyguardStateControllerCallback();
- mController.onViewDetached();
-
- verify(mStatusBarStateController).removeCallback(mStatusBarStateListener);
- verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback);
- }
-
- @Test
- public void testShouldPauseAuthUnpausedAlpha0() {
- mController.onViewAttached();
- captureStatusBarStateListeners();
-
- when(mView.getUnpausedAlpha()).thenReturn(0);
- sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
- assertTrue(mController.shouldPauseAuth());
- }
-
- @Test
- public void testShouldNotPauseAuthOnKeyguard() {
- mController.onViewAttached();
- captureStatusBarStateListeners();
-
- sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
- assertFalse(mController.shouldPauseAuth());
- }
-
- @Test
- public void onBiometricAuthenticated_pauseAuth() {
- // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard)
- mController.onViewAttached();
- captureStatusBarStateListeners();
- sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
- // WHEN biometric is authenticated
- captureKeyguardStateControllerCallback();
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
- mKeyguardStateControllerCallback.onUnlockedChanged();
-
- // THEN pause auth
- assertTrue(mController.shouldPauseAuth());
- }
-
- @Test
- public void testShouldPauseAuthIsLaunchTransitionFadingAway() {
- // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard)
- mController.onViewAttached();
- captureStatusBarStateListeners();
- sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
- // WHEN isLaunchTransitionFadingAway=true
- captureKeyguardStateControllerCallback();
- when(mKeyguardStateController.isLaunchTransitionFadingAway()).thenReturn(true);
- mKeyguardStateControllerCallback.onLaunchTransitionFadingAwayChanged();
-
- // THEN pause auth
- assertTrue(mController.shouldPauseAuth());
- }
-
- @Test
- public void testShouldPauseAuthOnShadeLocked() {
- mController.onViewAttached();
- captureStatusBarStateListeners();
-
- sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
-
- assertTrue(mController.shouldPauseAuth());
- }
-
- @Test
- public void testShouldPauseAuthOnShade() {
- mController.onViewAttached();
- captureStatusBarStateListeners();
-
- // WHEN not on keyguard yet (shade = home)
- sendStatusBarStateChanged(StatusBarState.SHADE);
-
- // THEN pause auth
- assertTrue(mController.shouldPauseAuth());
- }
-
- @Test
- public void testShouldPauseAuthAnimatingScreenOffFromShade() {
- mController.onViewAttached();
- captureStatusBarStateListeners();
-
- // WHEN transitioning from home/shade => keyguard + animating screen off
- mStatusBarStateListener.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD);
- when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true);
-
- // THEN pause auth
- assertTrue(mController.shouldPauseAuth());
- }
-
- @Test
- public void testDoNotPauseAuthAnimatingScreenOffFromLS() {
- mController.onViewAttached();
- captureStatusBarStateListeners();
-
- // WHEN animating screen off transition from LS => AOD
- sendStatusBarStateChanged(StatusBarState.KEYGUARD);
- when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true);
-
- // THEN don't pause auth
- assertFalse(mController.shouldPauseAuth());
- }
-
- @Test
- public void testOverrideShouldPauseAuthOnShadeLocked() {
- mController.onViewAttached();
- captureStatusBarStateListeners();
-
- sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
- assertTrue(mController.shouldPauseAuth());
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
deleted file mode 100644
index 29a6e56891af..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ /dev/null
@@ -1,643 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF
-import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN
-import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor
-import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
-import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
-import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-@TestableLooper.RunWithLooper
-@kotlinx.coroutines.ExperimentalCoroutinesApi
-class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
- UdfpsKeyguardViewLegacyControllerBaseTest() {
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
-
- private val keyguardBouncerRepository = kosmos.fakeKeyguardBouncerRepository
- private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
-
- @Before
- override fun setUp() {
- allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread
- MockitoAnnotations.initMocks(this)
- super.setUp()
- }
-
- override fun createUdfpsKeyguardViewController(): UdfpsKeyguardViewControllerLegacy {
- mPrimaryBouncerInteractor = kosmos.primaryBouncerInteractor
- mAlternateBouncerInteractor = kosmos.alternateBouncerInteractor
- mKeyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
- mUdfpsOverlayInteractor = kosmos.udfpsOverlayInteractor
- return createUdfpsKeyguardViewController(/* useModernBouncer */ true)
- }
-
- @Test
- fun bouncerExpansionChange_fadeIn() =
- testScope.runTest {
- // GIVEN view is attached
- mController.onViewAttached()
- captureKeyguardStateControllerCallback()
- Mockito.reset(mView)
-
- // WHEN status bar expansion is 0
- val job = mController.listenForBouncerExpansion(this)
- keyguardBouncerRepository.setPrimaryShow(true)
- keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
- runCurrent()
-
- // THEN alpha is 0
- verify(mView).unpausedAlpha = 0
-
- job.cancel()
- }
-
- @Test
- fun bouncerExpansionChange_pauseAuth() =
- testScope.runTest {
- // GIVEN view is attached + on the keyguard
- mController.onViewAttached()
- captureStatusBarStateListeners()
- sendStatusBarStateChanged(StatusBarState.KEYGUARD)
- Mockito.reset(mView)
-
- // WHEN panelViewExpansion changes to hide
- whenever(mView.unpausedAlpha).thenReturn(0)
- val job = mController.listenForBouncerExpansion(this)
- keyguardBouncerRepository.setPrimaryShow(true)
- keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
- runCurrent()
-
- // THEN pause auth is updated to PAUSE
- verify(mView, Mockito.atLeastOnce()).setPauseAuth(true)
-
- job.cancel()
- }
-
- @Test
- fun bouncerExpansionChange_unpauseAuth() =
- testScope.runTest {
- // GIVEN view is attached + on the keyguard + panel expansion is 0f
- mController.onViewAttached()
- captureStatusBarStateListeners()
- sendStatusBarStateChanged(StatusBarState.KEYGUARD)
- Mockito.reset(mView)
-
- // WHEN panelViewExpansion changes to expanded
- whenever(mView.unpausedAlpha).thenReturn(255)
- val job = mController.listenForBouncerExpansion(this)
- keyguardBouncerRepository.setPrimaryShow(true)
- keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN)
- runCurrent()
-
- // THEN pause auth is updated to NOT pause
- verify(mView, Mockito.atLeastOnce()).setPauseAuth(false)
-
- job.cancel()
- }
-
- @Test
- fun shadeLocked_showAlternateBouncer_unpauseAuth() =
- testScope.runTest {
- // GIVEN view is attached + on the SHADE_LOCKED (udfps view not showing)
- mController.onViewAttached()
- captureStatusBarStateListeners()
- sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED)
-
- // WHEN alternate bouncer is requested
- val job = mController.listenForAlternateBouncerVisibility(this)
- keyguardBouncerRepository.setAlternateVisible(true)
- runCurrent()
-
- // THEN udfps view will animate in & pause auth is updated to NOT pause
- verify(mView).animateInUdfpsBouncer(any())
- assertFalse(mController.shouldPauseAuth())
-
- job.cancel()
- }
-
- /** After migration to MODERN_BOUNCER, replaces UdfpsKeyguardViewControllerTest version */
- @Test
- fun shouldPauseAuthBouncerShowing() =
- testScope.runTest {
- // GIVEN view attached and we're on the keyguard
- mController.onViewAttached()
- captureStatusBarStateListeners()
- sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-
- // WHEN the bouncer expansion is VISIBLE
- val job = mController.listenForBouncerExpansion(this)
- keyguardBouncerRepository.setPrimaryShow(true)
- keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
- runCurrent()
-
- // THEN UDFPS shouldPauseAuth == true
- assertTrue(mController.shouldPauseAuth())
-
- job.cancel()
- }
-
- @Test
- fun shouldHandleTouchesChange() =
- testScope.runTest {
- val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches)
-
- // GIVEN view is attached + on the keyguard
- mController.onViewAttached()
- captureStatusBarStateListeners()
- sendStatusBarStateChanged(StatusBarState.KEYGUARD)
- whenever(mView.setPauseAuth(true)).thenReturn(true)
- whenever(mView.unpausedAlpha).thenReturn(0)
-
- // WHEN panelViewExpansion changes to expanded
- val job = mController.listenForBouncerExpansion(this)
- keyguardBouncerRepository.setPrimaryShow(true)
- keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
- runCurrent()
-
- // THEN UDFPS auth is paused and should not handle touches
- assertThat(mController.shouldPauseAuth()).isTrue()
- assertThat(shouldHandleTouches!!).isFalse()
-
- job.cancel()
- }
-
- @Test
- fun shouldHandleTouchesOnDetach() =
- testScope.runTest {
- val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches)
-
- // GIVEN view is attached + on the keyguard
- mController.onViewAttached()
- captureStatusBarStateListeners()
- sendStatusBarStateChanged(StatusBarState.KEYGUARD)
- whenever(mView.setPauseAuth(true)).thenReturn(true)
- whenever(mView.unpausedAlpha).thenReturn(0)
-
- // WHEN panelViewExpansion changes to expanded
- val job = mController.listenForBouncerExpansion(this)
- keyguardBouncerRepository.setPrimaryShow(true)
- keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
- runCurrent()
-
- mController.onViewDetached()
-
- // THEN UDFPS auth is paused and should not handle touches
- assertThat(mController.shouldPauseAuth()).isTrue()
- assertThat(shouldHandleTouches!!).isFalse()
-
- job.cancel()
- }
-
- @Test
- fun fadeFromDialogSuggestedAlpha() =
- testScope.runTest {
- // GIVEN view is attached and status bar expansion is 1f
- mController.onViewAttached()
- captureStatusBarStateListeners()
- val job = mController.listenForBouncerExpansion(this)
- keyguardBouncerRepository.setPrimaryShow(true)
- keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN)
- runCurrent()
- Mockito.reset(mView)
-
- // WHEN dialog suggested alpha is .6f
- whenever(mView.dialogSuggestedAlpha).thenReturn(.6f)
- sendStatusBarStateChanged(StatusBarState.KEYGUARD)
-
- // THEN alpha is updated based on dialog suggested alpha
- verify(mView).unpausedAlpha = (.6f * 255).toInt()
-
- job.cancel()
- }
-
- @Test
- fun transitionToFullShadeProgress() =
- testScope.runTest {
- // GIVEN view is attached and status bar expansion is 1f
- mController.onViewAttached()
- val job = mController.listenForBouncerExpansion(this)
- keyguardBouncerRepository.setPrimaryShow(true)
- keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN)
- runCurrent()
- Mockito.reset(mView)
- whenever(mView.dialogSuggestedAlpha).thenReturn(1f)
-
- // WHEN we're transitioning to the full shade
- val transitionProgress = .6f
- mController.setTransitionToFullShadeProgress(transitionProgress)
-
- // THEN alpha is between 0 and 255
- verify(mView).unpausedAlpha = ((1f - transitionProgress) * 255).toInt()
-
- job.cancel()
- }
-
- @Test
- fun aodToLockscreen_dozeAmountChanged() =
- testScope.runTest {
- // GIVEN view is attached
- mController.onViewAttached()
- Mockito.reset(mView)
-
- val job = mController.listenForLockscreenAodTransitions(this)
-
- // WHEN transitioning from lockscreen to aod
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- value = .3f,
- transitionState = TransitionState.RUNNING
- )
- )
- runCurrent()
- // THEN doze amount is updated
- verify(mView)
- .onDozeAmountChanged(
- eq(.3f),
- eq(.3f),
- eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
- )
-
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- value = 1f,
- transitionState = TransitionState.FINISHED
- )
- )
- runCurrent()
- // THEN doze amount is updated
- verify(mView)
- .onDozeAmountChanged(
- eq(1f),
- eq(1f),
- eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
- )
-
- job.cancel()
- }
-
- @Test
- fun lockscreenToAod_dozeAmountChanged() =
- testScope.runTest {
- // GIVEN view is attached
- mController.onViewAttached()
- Mockito.reset(mView)
-
- val job = mController.listenForLockscreenAodTransitions(this)
-
- // WHEN transitioning from lockscreen to aod
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- value = .3f,
- transitionState = TransitionState.RUNNING
- )
- )
- runCurrent()
- // THEN doze amount is updated
- verify(mView)
- .onDozeAmountChanged(
- eq(.3f),
- eq(.3f),
- eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
- )
-
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- value = 1f,
- transitionState = TransitionState.FINISHED
- )
- )
- runCurrent()
- // THEN doze amount is updated
- verify(mView)
- .onDozeAmountChanged(
- eq(1f),
- eq(1f),
- eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)
- )
-
- job.cancel()
- }
-
- @Test
- fun goneToAod_dozeAmountChanged() =
- testScope.runTest {
- // GIVEN view is attached
- mController.onViewAttached()
- Mockito.reset(mView)
-
- val job = mController.listenForGoneToAodTransition(this)
-
- // WHEN transitioning from lockscreen to aod
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- value = .3f,
- transitionState = TransitionState.RUNNING
- )
- )
- runCurrent()
- // THEN doze amount is updated
- verify(mView)
- .onDozeAmountChanged(
- eq(.3f),
- eq(.3f),
- eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF)
- )
-
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- value = 1f,
- transitionState = TransitionState.FINISHED
- )
- )
- runCurrent()
- // THEN doze amount is updated
- verify(mView)
- .onDozeAmountChanged(
- eq(1f),
- eq(1f),
- eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF)
- )
-
- job.cancel()
- }
-
- @Test
- fun aodToOccluded_dozeAmountChanged() =
- testScope.runTest {
- // GIVEN view is attached
- mController.onViewAttached()
- Mockito.reset(mView)
-
- val job = mController.listenForAodToOccludedTransitions(this)
-
- // WHEN transitioning from aod to occluded
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.AOD,
- to = KeyguardState.OCCLUDED,
- value = .3f,
- transitionState = TransitionState.RUNNING
- )
- )
- runCurrent()
- // THEN doze amount is updated
- verify(mView)
- .onDozeAmountChanged(eq(.7f), eq(.7f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE))
-
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.AOD,
- to = KeyguardState.OCCLUDED,
- value = 1f,
- transitionState = TransitionState.FINISHED
- )
- )
- runCurrent()
- // THEN doze amount is updated
- verify(mView)
- .onDozeAmountChanged(eq(0f), eq(0f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE))
-
- job.cancel()
- }
-
- @Test
- fun occludedToAod_dozeAmountChanged() =
- testScope.runTest {
- // GIVEN view is attached
- mController.onViewAttached()
- Mockito.reset(mView)
-
- val job = mController.listenForOccludedToAodTransition(this)
-
- // WHEN transitioning from occluded to aod
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.AOD,
- value = .3f,
- transitionState = TransitionState.RUNNING
- )
- )
- runCurrent()
- // THEN doze amount is updated
- verify(mView)
- .onDozeAmountChanged(
- eq(.3f),
- eq(.3f),
- eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF)
- )
-
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.AOD,
- value = 1f,
- transitionState = TransitionState.FINISHED
- )
- )
- runCurrent()
- // THEN doze amount is updated
- verify(mView)
- .onDozeAmountChanged(
- eq(1f),
- eq(1f),
- eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF)
- )
-
- job.cancel()
- }
-
- @Test
- fun cancelledAodToLockscreen_dozeAmountChangedToZero() =
- testScope.runTest {
- // GIVEN view is attached
- mController.onViewAttached()
- val job = mController.listenForLockscreenAodTransitions(this)
- runCurrent()
- Mockito.reset(mView)
-
- // WHEN aod to lockscreen transition is cancelled
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.AOD,
- to = KeyguardState.LOCKSCREEN,
- value = 1f,
- transitionState = TransitionState.CANCELED
- )
- )
- runCurrent()
- // ... and WHEN the next transition is from lockscreen => occluded
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.OCCLUDED,
- value = .4f,
- transitionState = TransitionState.STARTED
- )
- )
- runCurrent()
-
- // THEN doze amount is updated to zero
- verify(mView)
- .onDozeAmountChanged(eq(0f), eq(0f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN))
- job.cancel()
- }
-
- @Test
- fun cancelledLockscreenToAod_dozeAmountNotUpdatedToZero() =
- testScope.runTest {
- // GIVEN view is attached
- mController.onViewAttached()
- val job = mController.listenForLockscreenAodTransitions(this)
- runCurrent()
- Mockito.reset(mView)
-
- // WHEN lockscreen to aod transition is cancelled
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- value = 1f,
- transitionState = TransitionState.CANCELED
- )
- )
- runCurrent()
-
- // THEN doze amount is NOT updated to zero
- verify(mView, never()).onDozeAmountChanged(eq(0f), eq(0f), anyInt())
- job.cancel()
- }
-
- @Test
- fun dreamingToAod_dozeAmountChanged() =
- testScope.runTest {
- // GIVEN view is attached
- mController.onViewAttached()
- Mockito.reset(mView)
-
- val job = mController.listenForDreamingToAodTransitions(this)
- // WHEN dreaming to aod transition in progress
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.DREAMING,
- to = KeyguardState.AOD,
- value = .3f,
- transitionState = TransitionState.RUNNING
- )
- )
- runCurrent()
-
- // THEN doze amount is updated to
- verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF))
- job.cancel()
- }
-
- @Test
- fun alternateBouncerToAod_dozeAmountChanged() =
- testScope.runTest {
- // GIVEN view is attached
- mController.onViewAttached()
- Mockito.reset(mView)
-
- val job = mController.listenForAlternateBouncerToAodTransitions(this)
- // WHEN alternate bouncer to aod transition in progress
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.AOD,
- value = .3f,
- transitionState = TransitionState.RUNNING
- )
- )
- runCurrent()
-
- // THEN doze amount is updated to
- verify(mView)
- .onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN))
- job.cancel()
- }
-
- @Test
- fun bouncerToAod_dozeAmountChanged() =
- testScope.runTest {
- // GIVEN view is attached
- mController.onViewAttached()
- Mockito.reset(mView)
-
- val job = mController.listenForPrimaryBouncerToAodTransitions(this)
- // WHEN alternate bouncer to aod transition in progress
- transitionRepository.sendTransitionStep(
- TransitionStep(
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.AOD,
- value = .3f,
- transitionState = TransitionState.RUNNING
- )
- )
- runCurrent()
-
- // THEN doze amount is updated to
- verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF))
- job.cancel()
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
index 68cfa28dabd7..82ff61795e98 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -16,12 +16,9 @@
package com.android.systemui.bouncer.domain.interactor
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.keyguardUpdateMonitor
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
@@ -61,12 +58,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
underTest = kosmos.alternateBouncerInteractor
}
- @Test(expected = IllegalStateException::class)
- @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- fun enableUdfpsRefactor_deprecatedShowMethod_throwsIllegalStateException() {
- underTest.show()
- }
-
@Test
@DisableSceneContainer
fun canShowAlternateBouncer_false_dueToTransitionState() =
@@ -101,21 +92,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
}
@Test
- @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- fun canShowAlternateBouncerForFingerprint_givenCanShow() {
- givenCanShowAlternateBouncer()
- assertTrue(underTest.canShowAlternateBouncerForFingerprint())
- }
-
- @Test
- fun canShowAlternateBouncerForFingerprint_alternateBouncerUIUnavailable() {
- givenCanShowAlternateBouncer()
- kosmos.keyguardBouncerRepository.setAlternateBouncerUIAvailable(false)
-
- assertFalse(underTest.canShowAlternateBouncerForFingerprint())
- }
-
- @Test
fun canShowAlternateBouncerForFingerprint_ifFingerprintIsNotUsuallyAllowed() {
givenCanShowAlternateBouncer()
kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
@@ -140,15 +116,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
}
@Test
- @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- fun show_whenCanShow() {
- givenCanShowAlternateBouncer()
-
- assertTrue(underTest.show())
- assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
- }
-
- @Test
fun canShowAlternateBouncerForFingerprint_butCanDismissLockScreen() {
givenCanShowAlternateBouncer()
whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(true)
@@ -165,15 +132,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
}
@Test
- @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- fun show_whenCannotShow() {
- givenCannotShowAlternateBouncer()
-
- assertFalse(underTest.show())
- assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value)
- }
-
- @Test
fun hide_wasPreviouslyShowing() {
kosmos.keyguardBouncerRepository.setAlternateVisible(true)
@@ -190,7 +148,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
}
@Test
- @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
fun canShowAlternateBouncerForFingerprint_rearFps() {
givenCanShowAlternateBouncer()
kosmos.fingerprintPropertyRepository.supportsRearFps() // does not support alternate bouncer
@@ -198,29 +155,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
assertFalse(underTest.canShowAlternateBouncerForFingerprint())
}
- @Test
- @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- fun alternateBouncerUiAvailable_fromMultipleSources() {
- assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
-
- // GIVEN there are two different sources indicating the alternate bouncer is available
- underTest.setAlternateBouncerUIAvailable(true, "source1")
- underTest.setAlternateBouncerUIAvailable(true, "source2")
- assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
-
- // WHEN one of the sources no longer says the UI is available
- underTest.setAlternateBouncerUIAvailable(false, "source1")
-
- // THEN alternate bouncer UI is still available (from the other source)
- assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
-
- // WHEN all sources say the UI is not available
- underTest.setAlternateBouncerUIAvailable(false, "source2")
-
- // THEN alternate boucer UI is not available
- assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value)
- }
-
private fun givenAlternateBouncerSupported() {
kosmos.givenAlternateBouncerSupported()
}
@@ -228,8 +162,4 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
private fun givenCanShowAlternateBouncer() {
kosmos.givenCanShowAlternateBouncer()
}
-
- private fun givenCannotShowAlternateBouncer() {
- kosmos.givenCannotShowAlternateBouncer()
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index ee65fbd810ae..1e8651683ec1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal
+import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
@@ -77,7 +78,11 @@ class CommunalSceneStartableTest : SysuiTestCase() {
@Before
fun setUp() {
with(kosmos) {
- fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT)
+ fakeSettings.putIntForUser(
+ Settings.System.SCREEN_OFF_TIMEOUT,
+ SCREEN_TIMEOUT,
+ UserHandle.USER_CURRENT,
+ )
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt
index faaa4c415d28..1dd8ca9221a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt
@@ -14,23 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.window
+package com.android.systemui.display.data.repository
-import android.platform.test.annotations.EnableFlags
import android.view.Display
-import android.view.WindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
-import com.android.app.viewcapture.mockViewCaptureAwareWindowManager
import com.android.systemui.SysuiTestCase
-import com.android.systemui.display.data.repository.displayRepository
-import com.android.systemui.display.data.repository.fakeDisplayWindowPropertiesRepository
-import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.unconfinedTestDispatcher
-import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.runBlocking
@@ -43,28 +35,13 @@ import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
@SmallTest
-@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
-class MultiDisplayStatusBarWindowControllerStoreTest : SysuiTestCase() {
+class PerDisplayStoreImplTest : SysuiTestCase() {
private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
private val testScope = kosmos.testScope
private val fakeDisplayRepository = kosmos.displayRepository
- private val store =
- MultiDisplayStatusBarWindowControllerStore(
- backgroundApplicationScope = kosmos.applicationCoroutineScope,
- controllerFactory = kosmos.fakeStatusBarWindowControllerFactory,
- displayWindowPropertiesRepository = kosmos.fakeDisplayWindowPropertiesRepository,
- viewCaptureAwareWindowManagerFactory =
- object : ViewCaptureAwareWindowManager.Factory {
- override fun create(
- windowManager: WindowManager
- ): ViewCaptureAwareWindowManager {
- return kosmos.mockViewCaptureAwareWindowManager
- }
- },
- displayRepository = fakeDisplayRepository,
- )
+ private val store = kosmos.fakePerDisplayStore
@Before
fun start() {
@@ -80,34 +57,52 @@ class MultiDisplayStatusBarWindowControllerStoreTest : SysuiTestCase() {
@Test
fun forDisplay_defaultDisplay_multipleCalls_returnsSameInstance() =
testScope.runTest {
- val controller = store.defaultDisplay
+ val instance = store.defaultDisplay
- assertThat(store.defaultDisplay).isSameInstanceAs(controller)
+ assertThat(store.defaultDisplay).isSameInstanceAs(instance)
}
@Test
fun forDisplay_nonDefaultDisplay_multipleCalls_returnsSameInstance() =
testScope.runTest {
- val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
+ val instance = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
- assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isSameInstanceAs(controller)
+ assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isSameInstanceAs(instance)
}
@Test
fun forDisplay_nonDefaultDisplay_afterDisplayRemoved_returnsNewInstance() =
testScope.runTest {
- val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
+ val instance = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
fakeDisplayRepository.removeDisplay(NON_DEFAULT_DISPLAY_ID)
fakeDisplayRepository.addDisplay(createDisplay(NON_DEFAULT_DISPLAY_ID))
- assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(controller)
+ assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(instance)
}
@Test(expected = IllegalArgumentException::class)
fun forDisplay_nonExistingDisplayId_throws() =
testScope.runTest { store.forDisplay(NON_EXISTING_DISPLAY_ID) }
+ @Test
+ fun forDisplay_afterDisplayRemoved_onDisplayRemovalActionInvoked() =
+ testScope.runTest {
+ val instance = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
+
+ fakeDisplayRepository.removeDisplay(NON_DEFAULT_DISPLAY_ID)
+
+ assertThat(store.removalActions).containsExactly(instance)
+ }
+
+ @Test
+ fun forDisplay_withoutDisplayRemoval_onDisplayRemovalActionIsNotInvoked() =
+ testScope.runTest {
+ store.forDisplay(NON_DEFAULT_DISPLAY_ID)
+
+ assertThat(store.removalActions).isEmpty()
+ }
+
private fun createDisplay(displayId: Int): Display = mock {
on { getDisplayId() } doReturn displayId
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java
index fad52e090c69..ba43a236b8f9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/doze/DozeSuppressorTest.java
@@ -38,9 +38,9 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.testing.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
index 2a2a82d53671..b5ea305544ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -39,7 +39,6 @@ import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsReposi
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeTrustRepository
import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
@@ -100,18 +99,14 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
FakeTrustRepository(),
testScope.backgroundScope,
mSelectedUserInteractor,
- faceAuthInteractor
+ faceAuthInteractor,
)
alternateBouncerInteractor =
AlternateBouncerInteractor(
- mock(StatusBarStateController::class.java),
- mock(KeyguardStateController::class.java),
bouncerRepository,
FakeFingerprintPropertyRepository(),
- biometricSettingsRepository,
FakeSystemClock(),
- keyguardUpdateMonitor,
{ mock(DeviceEntryBiometricsAllowedInteractor::class.java) },
{ mock(KeyguardInteractor::class.java) },
{ mock(KeyguardTransitionInteractor::class.java) },
@@ -121,13 +116,12 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
underTest =
DeviceEntrySideFpsOverlayInteractor(
- testScope.backgroundScope,
mContext,
deviceEntryFingerprintAuthRepository,
kosmos.sceneInteractor,
primaryBouncerInteractor,
alternateBouncerInteractor,
- keyguardUpdateMonitor
+ keyguardUpdateMonitor,
)
}
@@ -142,7 +136,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
isShowing = true,
isAnimatingAway = false,
fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
+ isUnlockingWithFpAllowed = true,
)
assertThat(showIndicatorForDeviceEntry).isTrue()
}
@@ -158,7 +152,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
isShowing = false,
isAnimatingAway = false,
fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
+ isUnlockingWithFpAllowed = true,
)
assertThat(showIndicatorForDeviceEntry).isFalse()
}
@@ -169,13 +163,12 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
testScope.runTest {
underTest =
DeviceEntrySideFpsOverlayInteractor(
- testScope.backgroundScope,
mContext,
deviceEntryFingerprintAuthRepository,
kosmos.sceneInteractor,
primaryBouncerInteractor,
alternateBouncerInteractor,
- keyguardUpdateMonitor
+ keyguardUpdateMonitor,
)
val showIndicatorForDeviceEntry by
@@ -185,7 +178,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
updateBouncerScene(
isActive = true,
fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
+ isUnlockingWithFpAllowed = true,
)
assertThat(showIndicatorForDeviceEntry).isTrue()
}
@@ -196,13 +189,12 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
testScope.runTest {
underTest =
DeviceEntrySideFpsOverlayInteractor(
- testScope.backgroundScope,
mContext,
deviceEntryFingerprintAuthRepository,
kosmos.sceneInteractor,
primaryBouncerInteractor,
alternateBouncerInteractor,
- keyguardUpdateMonitor
+ keyguardUpdateMonitor,
)
val showIndicatorForDeviceEntry by
@@ -212,7 +204,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
updateBouncerScene(
isActive = false,
fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
+ isUnlockingWithFpAllowed = true,
)
assertThat(showIndicatorForDeviceEntry).isFalse()
}
@@ -228,7 +220,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
isShowing = true,
isAnimatingAway = false,
fpsDetectionRunning = false,
- isUnlockingWithFpAllowed = true
+ isUnlockingWithFpAllowed = true,
)
assertThat(showIndicatorForDeviceEntry).isFalse()
}
@@ -245,7 +237,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
isShowing = true,
isAnimatingAway = false,
fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = false
+ isUnlockingWithFpAllowed = false,
)
assertThat(showIndicatorForDeviceEntry).isFalse()
}
@@ -261,7 +253,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
updateBouncerScene(
isActive = true,
fpsDetectionRunning = false,
- isUnlockingWithFpAllowed = true
+ isUnlockingWithFpAllowed = true,
)
assertThat(showIndicatorForDeviceEntry).isFalse()
}
@@ -277,7 +269,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
updateBouncerScene(
isActive = true,
fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = false
+ isUnlockingWithFpAllowed = false,
)
assertThat(showIndicatorForDeviceEntry).isFalse()
}
@@ -294,7 +286,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
isShowing = true,
isAnimatingAway = true,
fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
+ isUnlockingWithFpAllowed = true,
)
assertThat(showIndicatorForDeviceEntry).isFalse()
}
@@ -325,7 +317,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
isShowing = true,
isAnimatingAway = false,
fpsDetectionRunning = true,
- isUnlockingWithFpAllowed = true
+ isUnlockingWithFpAllowed = true,
)
// Another request to show indicator for deviceEntryFingerprintAuthRepository update
@@ -355,7 +347,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
.thenReturn(isUnlockingWithFpAllowed)
mContext.orCreateTestableResources.addOverride(
R.bool.config_show_sidefps_hint_on_bouncer,
- true
+ true,
)
}
@@ -366,7 +358,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
) {
kosmos.sceneInteractor.changeScene(
if (isActive) Scenes.Bouncer else Scenes.Lockscreen,
- "reason"
+ "reason",
)
whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning)
@@ -375,7 +367,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
.thenReturn(isUnlockingWithFpAllowed)
mContext.orCreateTestableResources.addOverride(
R.bool.config_show_sidefps_hint_on_bouncer,
- true
+ true,
)
runCurrent()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
index c4eabd84e031..380060865282 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.keyguard.ui.binder
-import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper
import android.view.View
import android.view.layoutInflater
@@ -24,7 +23,6 @@ import android.view.mockedLayoutInflater
import android.view.windowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.givenCanShowAlternateBouncer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -63,7 +61,7 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() {
kosmos.mockedLayoutInflater.inflate(
eq(R.layout.alternate_bouncer),
isNull(),
- anyBoolean()
+ anyBoolean(),
)
)
.thenReturn(mockedAltBouncerView)
@@ -71,7 +69,6 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() {
}
@Test
- @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
fun addViewToWindowManager() {
testScope.runTest {
kosmos.givenCanShowAlternateBouncer()
@@ -85,7 +82,6 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() {
}
@Test
- @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
fun viewRemovedImmediatelyIfAlreadyAttachedToWindow() {
testScope.runTest {
kosmos.givenCanShowAlternateBouncer()
@@ -107,7 +103,6 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() {
}
@Test
- @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
fun viewNotRemovedUntilAttachedToWindow() {
testScope.runTest {
kosmos.givenCanShowAlternateBouncer()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
index c1bd37811787..5d9548057bae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
@@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
import com.android.systemui.coroutines.collectLastValue
@@ -34,7 +33,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
@@ -49,7 +47,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
@Test
fun alternateBouncerTransition_alternateBouncerWindowRequiredTrue() =
testScope.runTest {
- mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
fingerprintPropertyRepository.supportsUdfps()
@@ -64,28 +61,7 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
assertThat(alternateBouncerWindowRequired).isTrue()
transitionRepository.sendTransitionSteps(
- listOf(
- stepFromAlternateBouncer(1.0f, TransitionState.FINISHED),
- ),
- testScope,
- )
- assertThat(alternateBouncerWindowRequired).isFalse()
- }
-
- @Test
- fun deviceEntryUdfpsFlagDisabled_alternateBouncerWindowRequiredFalse() =
- testScope.runTest {
- mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- val alternateBouncerWindowRequired by
- collectLastValue(underTest.alternateBouncerWindowRequired)
- fingerprintPropertyRepository.supportsUdfps()
- transitionRepository.sendTransitionSteps(
- listOf(
- stepFromAlternateBouncer(0f, TransitionState.STARTED),
- stepFromAlternateBouncer(.4f),
- stepFromAlternateBouncer(.6f),
- stepFromAlternateBouncer(1f),
- ),
+ listOf(stepFromAlternateBouncer(1.0f, TransitionState.FINISHED)),
testScope,
)
assertThat(alternateBouncerWindowRequired).isFalse()
@@ -94,7 +70,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
@Test
fun lockscreenTransition_alternateBouncerWindowRequiredFalse() =
testScope.runTest {
- mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
fingerprintPropertyRepository.supportsUdfps()
@@ -113,7 +88,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
@Test
fun rearFps_alternateBouncerWindowRequiredFalse() =
testScope.runTest {
- mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
fingerprintPropertyRepository.supportsRearFps()
@@ -131,7 +105,7 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
private fun stepFromAlternateBouncer(
value: Float,
- state: TransitionState = TransitionState.RUNNING
+ state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return step(
from = KeyguardState.ALTERNATE_BOUNCER,
@@ -143,7 +117,7 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
private fun stepFromDozingToLockscreen(
value: Float,
- state: TransitionState = TransitionState.RUNNING
+ state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return step(
from = KeyguardState.DOZING,
@@ -157,14 +131,14 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
from: KeyguardState,
to: KeyguardState,
value: Float,
- transitionState: TransitionState
+ transitionState: TransitionState,
): TransitionStep {
return TransitionStep(
from = from,
to = to,
value = value,
transitionState = transitionState,
- ownerName = "AlternateBouncerViewModelTest"
+ ownerName = "AlternateBouncerViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt
index 14d60943149f..e5bdc2e56b11 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt
@@ -54,7 +54,7 @@ class PaginatedGridRepositoryTest : SysuiTestCase() {
private fun setRowsInConfig(rows: Int) =
with(kosmos) {
testCase.context.orCreateTestableResources.addOverride(
- R.integer.quick_settings_max_rows,
+ R.integer.quick_settings_paginated_grid_num_rows,
rows,
)
fakeConfigurationRepository.onConfigurationChange()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt
index ae6f576bcf3e..cda3d488cb1e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt
@@ -54,7 +54,7 @@ class QuickQuickSettingsRowRepositoryTest : SysuiTestCase() {
private fun setRowsInConfig(rows: Int) =
with(kosmos) {
testCase.context.orCreateTestableResources.addOverride(
- R.integer.quick_qs_panel_max_rows,
+ R.integer.quick_qs_paginated_grid_num_rows,
rows,
)
fakeConfigurationRepository.onConfigurationChange()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
index a1c0ef2789d5..2c894f9aa20f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt
@@ -151,7 +151,7 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() {
private fun Kosmos.setRows(rows: Int) {
testCase.context.orCreateTestableResources.addOverride(
- R.integer.quick_qs_panel_max_rows,
+ R.integer.quick_qs_paginated_grid_num_rows,
rows,
)
fakeConfigurationRepository.onConfigurationChange()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index 7798f46fdb46..b23262a87e2e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -34,9 +34,9 @@ import android.graphics.drawable.Drawable;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.service.quicksettings.Tile;
-import android.testing.UiThreadTest;
import android.widget.ImageView;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
new file mode 100644
index 000000000000..cdf6bda91301
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.systemui.qs.tiles.impl.hearingdevices.domain
+
+import android.graphics.drawable.TestStubDrawable
+import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.qs.tiles.impl.hearingdevices.qsHearingDevicesTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesTileMapperTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val qsTileConfig = kosmos.qsHearingDevicesTileConfig
+ private val mapper by lazy {
+ HearingDevicesTileMapper(
+ context.orCreateTestableResources
+ .apply { addOverride(R.drawable.qs_hearing_devices_icon, TestStubDrawable()) }
+ .resources,
+ context.theme,
+ )
+ }
+
+ @Test
+ fun map_anyActiveHearingDevice_anyPairedHearingDevice_activeState() {
+ val tileState: QSTileState =
+ mapper.map(
+ qsTileConfig,
+ HearingDevicesTileModel(
+ isAnyActiveHearingDevice = true,
+ isAnyPairedHearingDevice = true,
+ ),
+ )
+ val expectedState =
+ createHearingDevicesTileState(
+ QSTileState.ActivationState.ACTIVE,
+ context.getString(R.string.quick_settings_hearing_devices_connected),
+ )
+ QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
+ }
+
+ @Test
+ fun map_noActiveHearingDevice_anyPairedHearingDevice_inactiveState() {
+ val tileState: QSTileState =
+ mapper.map(
+ qsTileConfig,
+ HearingDevicesTileModel(
+ isAnyActiveHearingDevice = false,
+ isAnyPairedHearingDevice = true,
+ ),
+ )
+ val expectedState =
+ createHearingDevicesTileState(
+ QSTileState.ActivationState.INACTIVE,
+ context.getString(R.string.quick_settings_hearing_devices_disconnected),
+ )
+ QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
+ }
+
+ @Test
+ fun map_noActiveHearingDevice_noPairedHearingDevice_inactiveState() {
+ val tileState: QSTileState =
+ mapper.map(
+ qsTileConfig,
+ HearingDevicesTileModel(
+ isAnyActiveHearingDevice = false,
+ isAnyPairedHearingDevice = false,
+ ),
+ )
+ val expectedState =
+ createHearingDevicesTileState(QSTileState.ActivationState.INACTIVE, secondaryLabel = "")
+ QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
+ }
+
+ private fun createHearingDevicesTileState(
+ activationState: QSTileState.ActivationState,
+ secondaryLabel: String,
+ ): QSTileState {
+ val label = context.getString(R.string.quick_settings_hearing_devices_label)
+ val iconRes = R.drawable.qs_hearing_devices_icon
+ return QSTileState(
+ { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes,
+ label,
+ activationState,
+ secondaryLabel,
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+ label,
+ null,
+ QSTileState.SideViewIcon.Chevron,
+ QSTileState.EnabledState.ENABLED,
+ Switch::class.qualifiedName,
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
new file mode 100644
index 000000000000..1dfa2cd26491
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
@@ -0,0 +1,158 @@
+/*
+ * 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.systemui.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.EnabledOnRavenwood
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.statusbar.policy.fakeBluetoothController
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesTileDataInteractorTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val testUser = UserHandle.of(1)
+
+ private val controller = kosmos.fakeBluetoothController
+ private lateinit var underTest: HearingDevicesTileDataInteractor
+
+ @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+ @Mock private lateinit var checker: HearingDevicesChecker
+
+ @Before
+ fun setup() {
+ underTest = HearingDevicesTileDataInteractor(testScope.testScheduler, controller, checker)
+ }
+
+ @EnableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
+ @Test
+ fun availability_flagEnabled_returnTrue() =
+ testScope.runTest {
+ val availability by collectLastValue(underTest.availability(testUser))
+
+ assertThat(availability).isTrue()
+ }
+
+ @DisableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
+ @Test
+ fun availability_flagDisabled_returnFalse() =
+ testScope.runTest {
+ val availability by collectLastValue(underTest.availability(testUser))
+
+ assertThat(availability).isFalse()
+ }
+
+ @Test
+ fun tileData_bluetoothStateChanged_dataMatchesChecker() =
+ testScope.runTest {
+ val flowValues: List<HearingDevicesTileModel> by
+ collectValues(
+ underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup()
+
+ whenever(checker.isAnyPairedHearingDevice).thenReturn(false)
+ whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+ controller.isBluetoothEnabled = false
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value
+
+ whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+ whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+ controller.isBluetoothEnabled = true
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(2)
+
+ whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+ whenever(checker.isAnyActiveHearingDevice).thenReturn(true)
+ controller.isBluetoothEnabled = true
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(3)
+
+ assertThat(flowValues.map { it.isAnyPairedHearingDevice })
+ .containsExactly(false, true, true)
+ .inOrder()
+ assertThat(flowValues.map { it.isAnyActiveHearingDevice })
+ .containsExactly(false, false, true)
+ .inOrder()
+ }
+
+ @Test
+ fun tileData_bluetoothDeviceChanged_dataMatchesChecker() =
+ testScope.runTest {
+ val flowValues: List<HearingDevicesTileModel> by
+ collectValues(
+ underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup()
+
+ whenever(checker.isAnyPairedHearingDevice).thenReturn(false)
+ whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+ controller.onBluetoothDevicesChanged()
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value
+
+ whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+ whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+ controller.onBluetoothDevicesChanged()
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(2)
+
+ whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+ whenever(checker.isAnyActiveHearingDevice).thenReturn(true)
+ controller.onBluetoothDevicesChanged()
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(3)
+
+ assertThat(flowValues.map { it.isAnyPairedHearingDevice })
+ .containsExactly(false, true, true)
+ .inOrder()
+ assertThat(flowValues.map { it.isAnyActiveHearingDevice })
+ .containsExactly(false, false, true)
+ .inOrder()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
new file mode 100644
index 000000000000..00ee1c36590c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.systemui.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.platform.test.annotations.EnabledOnRavenwood
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
+import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.verify
+
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesTileUserActionInteractorTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val inputHandler = FakeQSTileIntentUserInputHandler()
+
+ private lateinit var underTest: HearingDevicesTileUserActionInteractor
+
+ @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+ @Mock private lateinit var dialogManager: HearingDevicesDialogManager
+
+ @Before
+ fun setUp() {
+ underTest =
+ HearingDevicesTileUserActionInteractor(
+ testScope.coroutineContext,
+ inputHandler,
+ dialogManager,
+ )
+ }
+
+ @Test
+ fun handleClick_launchDialog() =
+ testScope.runTest {
+ val input =
+ HearingDevicesTileModel(
+ isAnyActiveHearingDevice = true,
+ isAnyPairedHearingDevice = true,
+ )
+
+ underTest.handleInput(QSTileInputTestKtx.click(input))
+
+ verify(dialogManager).showDialog(anyOrNull(), eq(LAUNCH_SOURCE_QS_TILE))
+ }
+
+ @Test
+ fun handleLongClick_launchSettings() =
+ testScope.runTest {
+ val input =
+ HearingDevicesTileModel(
+ isAnyActiveHearingDevice = true,
+ isAnyPairedHearingDevice = true,
+ )
+
+ underTest.handleInput(QSTileInputTestKtx.longClick(input))
+
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+ assertThat(it.intent.action).isEqualTo(Settings.ACTION_HEARING_DEVICES_SETTINGS)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 0454317b5f04..06d19d7f9822 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -68,13 +68,13 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.LatencyTracker;
+import com.android.keyguard.EmptyLockIconViewController;
import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardClockSwitchController;
import com.android.keyguard.KeyguardSliceViewController;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardStatusViewController;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.LegacyLockIconViewController;
import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
@@ -271,6 +271,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock protected KeyguardUserSwitcherController mKeyguardUserSwitcherController;
@Mock protected KeyguardStatusViewComponent mKeyguardStatusViewComponent;
@Mock protected KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
+ @Mock protected EmptyLockIconViewController mLockIconViewController;
@Mock protected KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent;
@Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController;
@Mock protected KeyguardStatusBarViewController mKeyguardStatusBarViewController;
@@ -285,7 +286,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock protected AmbientState mAmbientState;
@Mock protected UserManager mUserManager;
@Mock protected UiEventLogger mUiEventLogger;
- @Mock protected LegacyLockIconViewController mLockIconViewController;
@Mock protected KeyguardViewConfigurator mKeyguardViewConfigurator;
@Mock protected KeyguardRootView mKeyguardRootView;
@Mock protected View mKeyguardRootViewChild;
@@ -397,7 +397,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
- mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
mMainDispatcher = getMainDispatcher();
KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps =
@@ -687,6 +686,9 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
when(longPressHandlingView.getResources()).thenReturn(longPressHandlingViewRes);
when(longPressHandlingViewRes.getString(anyInt())).thenReturn("");
+ when(mKeyguardRootView.findViewById(anyInt())).thenReturn(mKeyguardRootViewChild);
+ when(mKeyguardViewConfigurator.getKeyguardRootView()).thenReturn(mKeyguardRootView);
+
mNotificationPanelViewController = new NotificationPanelViewController(
mView,
mMainHandler,
@@ -852,7 +854,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mNotificationPanelViewController.mBottomAreaShadeAlphaAnimator.cancel();
mNotificationPanelViewController.cancelHeightAnimator();
leakedAnimators = mNotificationPanelViewController.mTestSetOfAnimatorsUsed.stream()
- .filter(Animator::isRunning).toList();
+ .filter(Animator::isRunning).toList();
mNotificationPanelViewController.mTestSetOfAnimatorsUsed.forEach(Animator::cancel);
}
if (mMainHandler != null) {
@@ -869,11 +871,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0);
when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(stackBottom);
when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom);
- when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding));
-
when(mKeyguardRootViewChild.getTop()).thenReturn((int) (stackBottom - lockIconPadding));
- when(mKeyguardRootView.findViewById(anyInt())).thenReturn(mKeyguardRootViewChild);
- when(mKeyguardViewConfigurator.getKeyguardRootView()).thenReturn(mKeyguardRootView);
when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding))
.thenReturn(indicationPadding);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 43dbb40d7721..ec75972aecfe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -217,7 +217,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(5);
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(5);
}
@@ -235,7 +234,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
}
@@ -253,7 +251,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
}
@@ -271,7 +268,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(2);
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(2);
}
@@ -289,7 +285,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
}
@@ -389,7 +384,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
@Test
@DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
public void alternateBouncerVisible_onTouchEvent_notHandled() {
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
// GIVEN alternate bouncer is visible
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 6f2302a22d7b..9fe52991c3a0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -30,7 +30,6 @@ import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityContainerController
-import com.android.keyguard.LegacyLockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.Flags
import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
@@ -54,7 +53,6 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.res.R
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.statusbar.DragDownHelper
@@ -71,7 +69,6 @@ import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.DozeScrimController
import com.android.systemui.statusbar.phone.DozeServiceHost
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
@@ -80,6 +77,7 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import java.util.Optional
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
@@ -98,11 +96,10 @@ import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
-import java.util.Optional
-import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -125,12 +122,10 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var ambientState: AmbientState
@Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
- @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
@Mock private lateinit var quickSettingsController: QuickSettingsControllerImpl
@Mock
private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
- @Mock private lateinit var lockIconViewController: LegacyLockIconViewController
@Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
@Mock private lateinit var pulsingGestureListener: PulsingGestureListener
@Mock
@@ -144,7 +139,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
@Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@Mock
private lateinit var unfoldTransitionProgressProvider:
- Optional<UnfoldTransitionProgressProvider>
+ Optional<UnfoldTransitionProgressProvider>
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock lateinit var dragDownHelper: DragDownHelper
@Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
@@ -176,20 +171,17 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
MockitoAnnotations.initMocks(this)
whenever(view.bottom).thenReturn(VIEW_BOTTOM)
whenever(view.findViewById<ViewGroup>(R.id.keyguard_bouncer_container))
- .thenReturn(mock(ViewGroup::class.java))
+ .thenReturn(mock(ViewGroup::class.java))
whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java)))
- .thenReturn(keyguardBouncerComponent)
+ .thenReturn(keyguardBouncerComponent)
whenever(keyguardBouncerComponent.securityContainerController)
- .thenReturn(keyguardSecurityContainerController)
+ .thenReturn(keyguardSecurityContainerController)
whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING)))
- .thenReturn(emptyFlow<TransitionStep>())
+ .thenReturn(emptyFlow<TransitionStep>())
featureFlagsClassic = FakeFeatureFlagsClassic()
featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
- if (!SceneContainerFlag.isEnabled) {
- mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- }
mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
testScope = TestScope()
@@ -208,9 +200,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
panelExpansionInteractor,
ShadeExpansionStateManager(),
stackScrollLayoutController,
- statusBarKeyguardViewManager,
statusBarWindowStateController,
- lockIconViewController,
centralSurfaces,
dozeServiceHost,
dozeScrimController,
@@ -233,7 +223,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
quickSettingsController,
primaryBouncerInteractor,
alternateBouncerInteractor,
- mock(BouncerViewBinder::class.java)
+ mock(BouncerViewBinder::class.java),
)
underTest.setupExpandedStatusBar()
underTest.setDragDownHelper(dragDownHelper)
@@ -294,7 +284,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true)
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(true)
+ .thenReturn(true)
whenever(phoneStatusBarViewController.sendTouchToView(DOWN_EVENT)).thenReturn(true)
val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
@@ -309,7 +299,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
underTest.setStatusBarViewController(phoneStatusBarViewController)
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(true)
+ .thenReturn(true)
// Item we're testing
whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(false)
@@ -327,7 +317,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true)
// Item we're testing
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(false)
+ .thenReturn(false)
val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
@@ -341,7 +331,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
underTest.setStatusBarViewController(phoneStatusBarViewController)
whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true)
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(true)
+ .thenReturn(true)
// Item we're testing
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false)
@@ -358,7 +348,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true)
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
- .thenReturn(true)
+ .thenReturn(true)
// Down event first
interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
@@ -379,7 +369,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
// GIVEN touch dispatcher in a state that returns true
underTest.setStatusBarViewController(phoneStatusBarViewController)
whenever(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
- .thenReturn(true)
+ .thenReturn(true)
assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isTrue()
// WHEN launch animation is running for 2 seconds
@@ -432,47 +422,13 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
}
@Test
- fun shouldInterceptTouchEvent_statusBarKeyguardViewManagerShouldIntercept() {
- // down event should be intercepted by keyguardViewManager
- whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
- .thenReturn(true)
-
- // Then touch should not be intercepted
- val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
- assertThat(shouldIntercept).isTrue()
- }
-
- @Test
- @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- fun shouldInterceptTouchEvent_dozing_touchInLockIconArea_touchNotIntercepted() {
- // GIVEN dozing
- whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
- // AND alternate bouncer doesn't want the touch
- whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
- .thenReturn(false)
- // AND quick settings controller doesn't want it
- whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
- .thenReturn(false)
- // AND the lock icon wants the touch
- whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(true)
-
- // THEN touch should NOT be intercepted by NotificationShade
- assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse()
- }
-
- @Test
@EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun shouldInterceptTouchEvent_dozing_touchNotInLockIconArea_touchIntercepted() {
// GIVEN dozing
whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
- // AND alternate bouncer doesn't want the touch
- whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
- .thenReturn(false)
- // AND the lock icon does NOT want the touch
- whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
// AND quick settings controller doesn't want it
whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
- .thenReturn(false)
+ .thenReturn(false)
// THEN touch should be intercepted by NotificationShade
assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
@@ -483,14 +439,9 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
fun shouldInterceptTouchEvent_dozing_touchInStatusBar_touchIntercepted() {
// GIVEN dozing
whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
- // AND alternate bouncer doesn't want the touch
- whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
- .thenReturn(false)
- // AND the lock icon does NOT want the touch
- whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
// AND quick settings controller DOES want it
whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
- .thenReturn(true)
+ .thenReturn(true)
// THEN touch should be intercepted by NotificationShade
assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
@@ -503,20 +454,13 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
// AND pulsing
whenever(dozeServiceHost.isPulsing()).thenReturn(true)
- // AND status bar doesn't want it
- whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
- .thenReturn(false)
- // AND shade is not fully expanded (mock is false by default)
- // AND the lock icon does NOT want the touch
- whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
// AND quick settings controller DOES want it
whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
- .thenReturn(true)
+ .thenReturn(true)
// AND bouncer is not showing
whenever(centralSurfaces.isBouncerShowing()).thenReturn(false)
// AND panel view controller wants it
- whenever(shadeViewController.handleExternalInterceptTouch(DOWN_EVENT))
- .thenReturn(true)
+ whenever(shadeViewController.handleExternalInterceptTouch(DOWN_EVENT)).thenReturn(true)
// THEN touch should be intercepted by NotificationShade
assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
@@ -589,12 +533,10 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
underTest.setupCommunalHubLayout()
// Simluate attaching the view so flow collection starts.
- val onAttachStateChangeListenerArgumentCaptor = ArgumentCaptor.forClass(
- View.OnAttachStateChangeListener::class.java
- )
- verify(view, atLeast(1)).addOnAttachStateChangeListener(
- onAttachStateChangeListenerArgumentCaptor.capture()
- )
+ val onAttachStateChangeListenerArgumentCaptor =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
+ verify(view, atLeast(1))
+ .addOnAttachStateChangeListener(onAttachStateChangeListenerArgumentCaptor.capture())
for (listener in onAttachStateChangeListenerArgumentCaptor.allValues) {
listener.onViewAttachedToWindow(view)
}
@@ -608,7 +550,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) :
@RequiresFlagsDisabled(Flags.FLAG_COMMUNAL_HUB)
fun doesNotSetupCommunalHubLayout_whenFlagDisabled() {
whenever(mGlanceableHubContainerController.communalAvailable())
- .thenReturn(MutableStateFlow(false))
+ .thenReturn(MutableStateFlow(false))
val mockCommunalPlaceholder = mock(View::class.java)
val fakeViewIndex = 20
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index ca29dd98a637..9093b2bcbbf8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -23,7 +23,6 @@ import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityContainerController
-import com.android.keyguard.LegacyLockIconViewController
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
@@ -57,7 +56,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.DozeScrimController
import com.android.systemui.statusbar.phone.DozeServiceHost
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
@@ -104,11 +102,9 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
@Mock
private lateinit var notificationStackScrollLayoutController:
NotificationStackScrollLayoutController
- @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
@Mock
private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
- @Mock private lateinit var lockIconViewController: LegacyLockIconViewController
@Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
@Mock private lateinit var ambientState: AmbientState
@Mock private lateinit var shadeLogger: ShadeLogger
@@ -161,7 +157,6 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
val featureFlags = FakeFeatureFlags()
featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true)
featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false)
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES)
testScope = TestScope()
controller =
@@ -176,9 +171,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
panelExpansionInteractor,
ShadeExpansionStateManager(),
notificationStackScrollLayoutController,
- statusBarKeyguardViewManager,
statusBarWindowStateController,
- lockIconViewController,
centralSurfaces,
dozeServiceHost,
dozeScrimController,
@@ -221,48 +214,18 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
}
@Test
- fun testInterceptTouchWhenShowingAltAuth() =
- testScope.runTest {
- captureInteractionEventHandler()
-
- // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
- whenever(statusBarStateController.isDozing).thenReturn(false)
- whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any())).thenReturn(true)
- whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
-
- // THEN we should intercept touch
- assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isTrue()
- }
-
- @Test
fun testNoInterceptTouch() =
testScope.runTest {
captureInteractionEventHandler()
- // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
+ // WHEN not dozing, drag down helper doesn't want to intercept
whenever(statusBarStateController.isDozing).thenReturn(false)
- whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any()))
- .thenReturn(false)
whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
// THEN we shouldn't intercept touch
assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isFalse()
}
- @Test
- fun testHandleTouchEventWhenShowingAltAuth() =
- testScope.runTest {
- captureInteractionEventHandler()
-
- // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
- whenever(statusBarStateController.isDozing).thenReturn(false)
- whenever(statusBarKeyguardViewManager.onTouch(any())).thenReturn(true)
- whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
-
- // THEN we should handle the touch
- assertThat(interactionEventHandler.handleTouchEvent(mock())).isTrue()
- }
-
private fun captureInteractionEventHandler() {
verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
interactionEventHandler = interactionEventHandlerCaptor.value
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt
index 118dea6376bb..69a76271f726 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
+package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel
import android.content.packageManager
import android.graphics.drawable.BitmapDrawable
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -40,13 +40,13 @@ import org.mockito.kotlin.any
import org.mockito.kotlin.whenever
@SmallTest
-class DemoRonChipViewModelTest : SysuiTestCase() {
+class DemoNotifChipViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val commandRegistry = kosmos.commandRegistry
private val pw = PrintWriter(StringWriter())
- private val underTest = kosmos.demoRonChipViewModel
+ private val underTest = kosmos.demoNotifChipViewModel
@Before
fun setUp() {
@@ -56,61 +56,61 @@ class DemoRonChipViewModelTest : SysuiTestCase() {
}
@Test
- @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
fun chip_flagOff_hidden() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
- addDemoRonChip()
+ addDemoNotifChip()
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
fun chip_noPackage_hidden() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
- commandRegistry.onShellCommand(pw, arrayOf("demo-ron"))
+ commandRegistry.onShellCommand(pw, arrayOf("demo-notif"))
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
fun chip_hasPackage_shown() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
- commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui"))
+ commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui"))
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
fun chip_hasText_shownWithText() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
commandRegistry.onShellCommand(
pw,
- arrayOf("demo-ron", "-p", "com.android.systemui", "-t", "test")
+ arrayOf("demo-notif", "-p", "com.android.systemui", "-t", "test"),
)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java)
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
fun chip_supportsColor() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
commandRegistry.onShellCommand(
pw,
- arrayOf("demo-ron", "-p", "com.android.systemui", "-c", "#434343")
+ arrayOf("demo-notif", "-p", "com.android.systemui", "-c", "#434343"),
)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
@@ -119,28 +119,28 @@ class DemoRonChipViewModelTest : SysuiTestCase() {
}
@Test
- @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
fun chip_hasHideArg_hidden() =
testScope.runTest {
val latest by collectLastValue(underTest.chip)
// First, show a chip
- addDemoRonChip()
+ addDemoNotifChip()
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
// Then, hide the chip
- commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "--hide"))
+ commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "--hide"))
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
}
- private fun addDemoRonChip() {
- Companion.addDemoRonChip(commandRegistry, pw)
+ private fun addDemoNotifChip() {
+ addDemoNotifChip(commandRegistry, pw)
}
companion object {
- fun addDemoRonChip(commandRegistry: CommandRegistry, pw: PrintWriter) {
- commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui"))
+ fun addDemoNotifChip(commandRegistry: CommandRegistry, pw: PrintWriter) {
+ commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui"))
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
new file mode 100644
index 000000000000..eb5d9318c88f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -0,0 +1,121 @@
+/*
+ * 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.systemui.statusbar.chips.notification.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+@EnableFlags(StatusBarNotifChips.FLAG_NAME)
+class NotifChipsViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val activeNotificationListRepository = kosmos.activeNotificationListRepository
+
+ private val underTest = kosmos.notifChipsViewModel
+
+ @Test
+ fun chips_noNotifs_empty() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ setNotifs(emptyList())
+
+ assertThat(latest).isEmpty()
+ }
+
+ @Test
+ fun chips_notifMissingStatusBarChipIconView_empty() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = null)))
+
+ assertThat(latest).isEmpty()
+ }
+
+ @Test
+ fun chips_oneNotif_statusBarIconViewMatches() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val icon = mock<StatusBarIconView>()
+ setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon)))
+
+ assertThat(latest).hasSize(1)
+ val chip = latest!![0]
+ assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+ assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon))
+ }
+
+ @Test
+ fun chips_twoNotifs_twoChips() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val firstIcon = mock<StatusBarIconView>()
+ val secondIcon = mock<StatusBarIconView>()
+ setNotifs(
+ listOf(
+ activeNotificationModel(key = "notif1", statusBarChipIcon = firstIcon),
+ activeNotificationModel(key = "notif2", statusBarChipIcon = secondIcon),
+ )
+ )
+
+ assertThat(latest).hasSize(2)
+ assertIsNotifChip(latest!![0], firstIcon)
+ assertIsNotifChip(latest!![1], secondIcon)
+ }
+
+ private fun setNotifs(notifs: List<ActiveNotificationModel>) {
+ activeNotificationListRepository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply { notifs.forEach { addIndividualNotif(it) } }
+ .build()
+ testScope.runCurrent()
+ }
+
+ companion object {
+ fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) {
+ assertThat(latest)
+ .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java)
+ assertThat((latest as OngoingActivityChipModel.Shown).icon)
+ .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon))
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 26ce7b956fde..e96def6d43a3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -25,7 +25,6 @@ import android.platform.test.annotations.DisableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
@@ -40,7 +39,8 @@ import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -66,13 +66,11 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
-/**
- * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is disabled.
- */
+/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is disabled. */
@SmallTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
-@DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+@DisableFlags(StatusBarNotifChips.FLAG_NAME)
class OngoingActivityChipsViewModelTest : SysuiTestCase() {
private val kosmos = Kosmos().also { it.testCase = this }
private val testScope = kosmos.testScope
@@ -99,11 +97,11 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
setUpPackageManagerForMediaProjection(kosmos)
- kosmos.demoRonChipViewModel.start()
+ kosmos.demoNotifChipViewModel.start()
val icon =
BitmapDrawable(
context.resources,
- Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888)
+ Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888),
)
whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon)
}
@@ -325,7 +323,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
latest: OngoingActivityChipModel?,
chipView: View,
dialog: SystemUIDialog,
- kosmos: Kosmos
+ kosmos: Kosmos,
): DialogInterface.OnClickListener {
// Capture the action that would get invoked when the user clicks "Stop" on the dialog
lateinit var dialogStopAction: DialogInterface.OnClickListener
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index 631120b39805..b12d7c57e1fd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -24,7 +24,6 @@ import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
@@ -34,10 +33,13 @@ import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager
import com.android.systemui.res.R
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModelTest.Companion.addDemoRonChip
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModelTest.Companion.addDemoNotifChip
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModelTest.Companion.assertIsNotifChip
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
@@ -46,6 +48,10 @@ import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsVie
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog
import com.android.systemui.statusbar.commandline.commandRegistry
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
@@ -67,14 +73,12 @@ import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
-/**
- * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is enabled.
- */
+/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is enabled. */
@SmallTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
-@EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
-class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
+@EnableFlags(StatusBarNotifChips.FLAG_NAME)
+class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val systemClock = kosmos.fakeSystemClock
@@ -83,6 +87,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState
private val callRepo = kosmos.ongoingCallRepository
+ private val activeNotificationListRepository = kosmos.activeNotificationListRepository
private val pw = PrintWriter(StringWriter())
@@ -103,16 +108,16 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
@Before
fun setUp() {
setUpPackageManagerForMediaProjection(kosmos)
- kosmos.demoRonChipViewModel.start()
+ kosmos.demoNotifChipViewModel.start()
val icon =
BitmapDrawable(
context.resources,
- Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888)
+ Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888),
)
whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon)
}
- // Even though the `primaryChip` flow isn't used when the RONs flag is on, still test that the
+ // Even though the `primaryChip` flow isn't used when the notifs flag is on, still test that the
// flow has the right behavior to verify that we don't break any existing functionality.
@Test
@@ -249,13 +254,13 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
testScope.runTest {
screenRecordState.value = ScreenRecordModel.Recording
callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
- addDemoRonChip(commandRegistry, pw)
+ addDemoNotifChip(commandRegistry, pw)
val latest by collectLastValue(underTest.chips)
assertIsScreenRecordChip(latest!!.primary)
assertIsCallChip(latest!!.secondary)
- // Demo RON chip is dropped
+ // Demo notif chip is dropped
}
@Test
@@ -288,10 +293,101 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
}
@Test
+ fun chips_singleNotifChip_primaryIsNotifSecondaryIsHidden() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val icon = mock<StatusBarIconView>()
+ setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon)))
+
+ assertIsNotifChip(latest!!.primary, icon)
+ assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+ }
+
+ @Test
+ fun chips_twoNotifChips_primaryAndSecondaryAreNotifsInOrder() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val firstIcon = mock<StatusBarIconView>()
+ val secondIcon = mock<StatusBarIconView>()
+ setNotifs(
+ listOf(
+ activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
+ activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon),
+ )
+ )
+
+ assertIsNotifChip(latest!!.primary, firstIcon)
+ assertIsNotifChip(latest!!.secondary, secondIcon)
+ }
+
+ @Test
+ fun chips_threeNotifChips_topTwoShown() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val firstIcon = mock<StatusBarIconView>()
+ val secondIcon = mock<StatusBarIconView>()
+ val thirdIcon = mock<StatusBarIconView>()
+ setNotifs(
+ listOf(
+ activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
+ activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon),
+ activeNotificationModel(key = "thirdNotif", statusBarChipIcon = thirdIcon),
+ )
+ )
+
+ assertIsNotifChip(latest!!.primary, firstIcon)
+ assertIsNotifChip(latest!!.secondary, secondIcon)
+ }
+
+ @Test
+ fun chips_callAndNotifs_primaryIsCallSecondaryIsNotif() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ val firstIcon = mock<StatusBarIconView>()
+ setNotifs(
+ listOf(
+ activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon),
+ activeNotificationModel(
+ key = "secondNotif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ ),
+ )
+ )
+
+ assertIsCallChip(latest!!.primary)
+ assertIsNotifChip(latest!!.secondary, firstIcon)
+ }
+
+ @Test
+ fun chips_screenRecordAndCallAndNotifs_notifsNotShown() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ screenRecordState.value = ScreenRecordModel.Recording
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = mock<StatusBarIconView>(),
+ )
+ )
+ )
+
+ assertIsScreenRecordChip(latest!!.primary)
+ assertIsCallChip(latest!!.secondary)
+ }
+
+ @Test
fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
testScope.runTest {
// Start with just the lowest priority chip shown
- addDemoRonChip(commandRegistry, pw)
+ addDemoNotifChip(commandRegistry, pw)
// And everything else hidden
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
mediaProjectionState.value = MediaProjectionState.NotProjecting
@@ -299,7 +395,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
val latest by collectLastValue(underTest.primaryChip)
- assertIsDemoRonChip(latest)
+ assertIsDemoNotifChip(latest)
// WHEN the higher priority call chip is added
callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
@@ -333,7 +429,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
- addDemoRonChip(commandRegistry, pw)
+ addDemoNotifChip(commandRegistry, pw)
val latest by collectLastValue(underTest.primaryChip)
@@ -355,15 +451,15 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
// WHEN the higher priority call is removed
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
- // THEN the lower priority demo RON is used
- assertIsDemoRonChip(latest)
+ // THEN the lower priority demo notif is used
+ assertIsDemoNotifChip(latest)
}
@Test
fun chips_movesChipsAroundAccordingToPriority() =
testScope.runTest {
// Start with just the lowest priority chip shown
- addDemoRonChip(commandRegistry, pw)
+ addDemoNotifChip(commandRegistry, pw)
// And everything else hidden
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
mediaProjectionState.value = MediaProjectionState.NotProjecting
@@ -371,16 +467,16 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
val latest by collectLastValue(underTest.chips)
- assertIsDemoRonChip(latest!!.primary)
+ assertIsDemoNotifChip(latest!!.primary)
assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
// WHEN the higher priority call chip is added
callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
- // THEN the higher priority call chip is used as primary and demo ron is demoted to
+ // THEN the higher priority call chip is used as primary and demo notif is demoted to
// secondary
assertIsCallChip(latest!!.primary)
- assertIsDemoRonChip(latest!!.secondary)
+ assertIsDemoNotifChip(latest!!.secondary)
// WHEN the higher priority media projection chip is added
mediaProjectionState.value =
@@ -391,7 +487,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
)
// THEN the higher priority media projection chip is used as primary and call is demoted
- // to secondary (and demo RON is dropped altogether)
+ // to secondary (and demo notif is dropped altogether)
assertIsShareToAppChip(latest!!.primary)
assertIsCallChip(latest!!.secondary)
@@ -405,15 +501,15 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
screenRecordState.value = ScreenRecordModel.DoingNothing
callRepo.setOngoingCallState(OngoingCallModel.NoCall)
- // THEN media projection and demo RON remain
+ // THEN media projection and demo notif remain
assertIsShareToAppChip(latest!!.primary)
- assertIsDemoRonChip(latest!!.secondary)
+ assertIsDemoNotifChip(latest!!.secondary)
// WHEN media projection is dropped
mediaProjectionState.value = MediaProjectionState.NotProjecting
- // THEN demo RON is promoted to primary
- assertIsDemoRonChip(latest!!.primary)
+ // THEN demo notif is promoted to primary
+ assertIsDemoNotifChip(latest!!.primary)
assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
}
@@ -526,9 +622,17 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() {
assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden(shouldAnimate = false))
}
- private fun assertIsDemoRonChip(latest: OngoingActivityChipModel?) {
+ private fun assertIsDemoNotifChip(latest: OngoingActivityChipModel?) {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
assertThat((latest as OngoingActivityChipModel.Shown).icon)
.isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java)
}
+
+ private fun setNotifs(notifs: List<ActiveNotificationModel>) {
+ activeNotificationListRepository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply { notifs.forEach { addIndividualNotif(it) } }
+ .build()
+ testScope.runCurrent()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
new file mode 100644
index 000000000000..8dcc44463213
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
@@ -0,0 +1,136 @@
+/*
+ * 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.systemui.statusbar.core
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Expect
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+class MultiDisplayStatusBarStarterTest : SysuiTestCase() {
+ @get:Rule val expect: Expect = Expect.create()
+
+ private val kosmos =
+ testKosmos().also {
+ it.statusBarOrchestratorFactory = it.fakeStatusBarOrchestratorFactory
+ it.statusBarInitializerStore = it.fakeStatusBarInitializerStore
+ }
+ private val testScope = kosmos.testScope
+ private val fakeDisplayRepository = kosmos.displayRepository
+ private val fakeOrchestratorFactory = kosmos.fakeStatusBarOrchestratorFactory
+ private val fakeInitializerStore = kosmos.fakeStatusBarInitializerStore
+
+ // Lazy, so that @EnableFlags is set before initializer is instantiated.
+ private val underTest by lazy { kosmos.multiDisplayStatusBarStarter }
+
+ @Test
+ fun start_startsInitializersForCurrentDisplays() =
+ testScope.runTest {
+ fakeDisplayRepository.addDisplay(displayId = 1)
+ fakeDisplayRepository.addDisplay(displayId = 2)
+
+ underTest.start()
+ runCurrent()
+
+ expect
+ .that(fakeInitializerStore.forDisplay(displayId = 1).startedByCoreStartable)
+ .isTrue()
+ expect
+ .that(fakeInitializerStore.forDisplay(displayId = 2).startedByCoreStartable)
+ .isTrue()
+ }
+
+ @Test
+ fun start_startsOrchestratorForCurrentDisplays() =
+ testScope.runTest {
+ fakeDisplayRepository.addDisplay(displayId = 1)
+ fakeDisplayRepository.addDisplay(displayId = 2)
+
+ underTest.start()
+ runCurrent()
+
+ verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 1)!!).start()
+ verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 2)!!).start()
+ }
+
+ @Test
+ fun displayAdded_orchestratorForNewDisplayIsStarted() =
+ testScope.runTest {
+ underTest.start()
+ runCurrent()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 3)!!).start()
+ }
+
+ @Test
+ fun displayAdded_initializerForNewDisplayIsStarted() =
+ testScope.runTest {
+ underTest.start()
+ runCurrent()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ expect
+ .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable)
+ .isTrue()
+ }
+
+ @Test
+ fun displayAddedDuringStart_initializerForNewDisplayIsStarted() =
+ testScope.runTest {
+ underTest.start()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ expect
+ .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable)
+ .isTrue()
+ }
+
+ @Test
+ fun displayAddedDuringStart_orchestratorForNewDisplayIsStarted() =
+ testScope.runTest {
+ underTest.start()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ expect
+ .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable)
+ .isTrue()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
index f64387c95e3d..c737bf7a8bec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
@@ -60,10 +60,9 @@ class StatusBarInitializerTest : SysuiTestCase() {
val underTest =
StatusBarInitializerImpl(
- displayId = context.displayId,
- statusBarWindowControllerStore = windowControllerStore,
collapsedStatusBarFragmentProvider = { mock(CollapsedStatusBarFragment::class.java) },
creationListeners = setOf(),
+ statusBarWindowController = windowController,
)
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
index bb3fb1e71f78..ab8e878ab309 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
@@ -38,13 +38,10 @@ import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT
import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT_TRANSPARENT
import com.android.systemui.statusbar.data.model.StatusBarMode.OPAQUE
import com.android.systemui.statusbar.data.model.StatusBarMode.TRANSPARENT
-import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
-import com.android.systemui.statusbar.phone.mockPhoneStatusBarTransitions
-import com.android.systemui.statusbar.phone.mockPhoneStatusBarViewController
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
-import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStateRepositoryStore
-import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
-import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository
+import com.android.systemui.statusbar.window.fakeStatusBarWindowController
import com.android.systemui.testKosmos
import com.android.wm.shell.bubbles.bubbles
import com.google.common.truth.Truth.assertThat
@@ -60,25 +57,20 @@ import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class StatusBarOrchestratorTest : SysuiTestCase() {
- private val kosmos =
- testKosmos().also {
- it.testDispatcher = it.unconfinedTestDispatcher
- it.statusBarWindowStateRepositoryStore = it.fakeStatusBarWindowStateRepositoryStore
- }
+ private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
private val testScope = kosmos.testScope
- private val statusBarViewController = kosmos.mockPhoneStatusBarViewController
- private val statusBarWindowControllerStore = kosmos.fakeStatusBarWindowControllerStore
- private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository
- private val pluginDependencyProvider = kosmos.mockPluginDependencyProvider
- private val notificationShadeWindowViewController =
+ private val fakeStatusBarModePerDisplayRepository = kosmos.fakeStatusBarModePerDisplayRepository
+ private val mockPluginDependencyProvider = kosmos.mockPluginDependencyProvider
+ private val mockNotificationShadeWindowViewController =
kosmos.mockNotificationShadeWindowViewController
- private val shadeSurface = kosmos.mockShadeSurface
- private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
- private val fakeStatusBarWindowStateRepositoryStore =
- kosmos.fakeStatusBarWindowStateRepositoryStore
+ private val mockShadeSurface = kosmos.mockShadeSurface
+ private val fakeBouncerRepository = kosmos.fakeKeyguardBouncerRepository
+ private val fakeStatusBarWindowStatePerDisplayRepository =
+ kosmos.fakeStatusBarWindowStatePerDisplayRepository
private val fakePowerRepository = kosmos.fakePowerRepository
- private val mockPhoneStatusBarTransitions = kosmos.mockPhoneStatusBarTransitions
private val mockBubbles = kosmos.bubbles
+ private val fakeStatusBarWindowController = kosmos.fakeStatusBarWindowController
+ private val fakeStatusBarInitializer = kosmos.fakeStatusBarInitializer
private val orchestrator = kosmos.statusBarOrchestrator
@@ -86,30 +78,31 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
fun start_setsUpPluginDependencies() {
orchestrator.start()
- verify(pluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java)
- verify(pluginDependencyProvider).allowPluginDependency(StatusBarStateController::class.java)
+ verify(mockPluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java)
+ verify(mockPluginDependencyProvider)
+ .allowPluginDependency(StatusBarStateController::class.java)
}
@Test
fun start_attachesWindow() {
orchestrator.start()
- assertThat(statusBarWindowControllerStore.defaultDisplay.isAttached).isTrue()
+ assertThat(fakeStatusBarWindowController.isAttached).isTrue()
}
@Test
fun start_setsStatusBarControllerOnShade() {
orchestrator.start()
- verify(notificationShadeWindowViewController)
- .setStatusBarViewController(statusBarViewController)
+ verify(mockNotificationShadeWindowViewController)
+ .setStatusBarViewController(fakeStatusBarInitializer.statusBarViewController)
}
@Test
fun start_updatesShadeExpansion() {
orchestrator.start()
- verify(shadeSurface).updateExpansionAndVisibility()
+ verify(mockShadeSurface).updateExpansionAndVisibility()
}
@Test
@@ -117,9 +110,9 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
testScope.runTest {
orchestrator.start()
- bouncerRepository.setPrimaryShow(isShowing = true)
+ fakeBouncerRepository.setPrimaryShow(isShowing = true)
- verify(statusBarViewController)
+ verify(fakeStatusBarInitializer.statusBarViewController)
.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS)
}
@@ -128,9 +121,9 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
testScope.runTest {
orchestrator.start()
- bouncerRepository.setPrimaryShow(isShowing = false)
+ fakeBouncerRepository.setPrimaryShow(isShowing = false)
- verify(statusBarViewController)
+ verify(fakeStatusBarInitializer.statusBarViewController)
.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO)
}
@@ -141,7 +134,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
orchestrator.start()
- verify(mockPhoneStatusBarTransitions).finishAnimations()
+ verify(fakeStatusBarInitializer.statusBarTransitions).finishAnimations()
}
@Test
@@ -151,7 +144,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
orchestrator.start()
- verify(mockPhoneStatusBarTransitions, never()).finishAnimations()
+ verify(fakeStatusBarInitializer.statusBarTransitions, never()).finishAnimations()
}
@Test
@@ -208,7 +201,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
orchestrator.start()
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
}
@@ -222,19 +215,19 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
orchestrator.start()
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
setStatusBarMode(OPAQUE)
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(OPAQUE.toTransitionModeInt(), /* animate= */ true)
setStatusBarMode(LIGHTS_OUT)
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(LIGHTS_OUT.toTransitionModeInt(), /* animate= */ true)
setStatusBarMode(LIGHTS_OUT_TRANSPARENT)
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(LIGHTS_OUT_TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
}
@@ -248,7 +241,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
orchestrator.start()
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
}
@@ -262,7 +255,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
orchestrator.start()
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
}
@@ -276,7 +269,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
orchestrator.start()
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
}
@@ -295,7 +288,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
setTransientStatusBar()
clearTransientStatusBar()
- verify(mockPhoneStatusBarTransitions, times(1))
+ verify(fakeStatusBarInitializer.statusBarTransitions, times(1))
.transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
}
@@ -318,18 +311,18 @@ class StatusBarOrchestratorTest : SysuiTestCase() {
}
private fun setTransientStatusBar() {
- statusBarModeRepository.defaultDisplay.showTransient()
+ fakeStatusBarModePerDisplayRepository.showTransient()
}
private fun clearTransientStatusBar() {
- statusBarModeRepository.defaultDisplay.clearTransient()
+ fakeStatusBarModePerDisplayRepository.clearTransient()
}
private fun setStatusBarWindowState(state: StatusBarWindowState) {
- fakeStatusBarWindowStateRepositoryStore.defaultDisplay.setWindowState(state)
+ fakeStatusBarWindowStatePerDisplayRepository.setWindowState(state)
}
private fun setStatusBarMode(statusBarMode: StatusBarMode) {
- statusBarModeRepository.defaultDisplay.statusBarMode.value = statusBarMode
+ fakeStatusBarModePerDisplayRepository.statusBarMode.value = statusBarMode
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
index 35e4047109d5..97fa6eb17b5b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt
@@ -31,6 +31,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() {
private var isStarted = false
+ private var wasStarted = false
private var scrimOffset = 0f
private var contentHeight = 0f
private var isCurrentGestureOverscroll = false
@@ -46,7 +47,10 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() {
minVisibleScrimHeight = { MIN_VISIBLE_SCRIM_HEIGHT },
isCurrentGestureOverscroll = { isCurrentGestureOverscroll },
onStart = { isStarted = true },
- onStop = { isStarted = false },
+ onStop = {
+ wasStarted = true
+ isStarted = false
+ },
)
@Test
@@ -180,6 +184,7 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() {
)
assertThat(offsetConsumed).isEqualTo(Offset.Zero)
+ assertThat(wasStarted).isEqualTo(false)
assertThat(isStarted).isEqualTo(false)
}
@@ -196,7 +201,9 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() {
)
assertThat(offsetConsumed).isEqualTo(Offset.Zero)
- assertThat(isStarted).isEqualTo(true)
+ // Returning 0 offset will immediately stop the connection
+ assertThat(wasStarted).isEqualTo(true)
+ assertThat(isStarted).isEqualTo(false)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
index d04d6fc5f593..0947cd5aaabb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
@@ -45,12 +45,12 @@ import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.testing.UiThreadTest;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index 22f1e4604bbd..6435e8203d3d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -24,9 +24,9 @@ import static org.mockito.Mockito.mock;
import android.provider.Settings;
import android.testing.TestableResources;
-import android.testing.UiThreadTest;
import android.util.KeyValueListParser;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index 8d678ef00b4a..bf144729dea3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -21,7 +21,6 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
-import com.android.app.tracing.coroutines.flow.map
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.DisableSceneContainer
@@ -51,6 +50,7 @@ import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.value
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index c804fc6990ae..ba5fb7f8df00 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -98,7 +98,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
@@ -135,7 +135,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
@@ -164,8 +164,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
val chipWidth = 30
val dotWidth = 10
val isRtl = false
- val contentRect =
- Rect(/* left = */ 0, /* top = */ 10, /* right = */ 1000, /* bottom = */ 100)
+ val contentRect = Rect(/* left= */ 0, /* top= */ 10, /* right= */ 1000, /* bottom= */ 100)
val chipBounds =
getPrivacyChipBoundingRectForInsets(contentRect, dotWidth, chipWidth, isRtl)
@@ -207,7 +206,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -228,7 +227,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -251,7 +250,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -263,7 +262,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
0,
screenBounds.height() - dcBounds.height() - dotWidth,
- sbHeightLandscape
+ sbHeightLandscape,
)
bounds =
@@ -278,7 +277,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -320,7 +319,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -331,7 +330,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
protectionBounds.bottom,
0,
screenBounds.height() - minRightPadding,
- sbHeightLandscape
+ sbHeightLandscape,
)
bounds =
@@ -346,7 +345,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -369,7 +368,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -381,7 +380,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
0,
screenBounds.height() - protectionBounds.bottom - dotWidth,
- sbHeightLandscape
+ sbHeightLandscape,
)
bounds =
@@ -396,7 +395,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -415,7 +414,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
left = screenBounds.right - dcWidth,
top = 0,
right = screenBounds.right,
- bottom = dcHeight
+ bottom = dcHeight,
)
val dcBoundsLandscape = Rect(left = 0, top = 0, right = dcHeight, bottom = dcWidth)
val dcBoundsSeascape =
@@ -423,14 +422,14 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
left = screenBounds.right - dcHeight,
top = screenBounds.bottom - dcWidth,
right = screenBounds.right - dcHeight,
- bottom = screenBounds.bottom - dcWidth
+ bottom = screenBounds.bottom - dcWidth,
)
val dcBoundsUpsideDown =
Rect(
left = 0,
top = screenBounds.bottom - dcHeight,
right = dcWidth,
- bottom = screenBounds.bottom - dcHeight
+ bottom = screenBounds.bottom - dcHeight,
)
val minLeftPadding = 20
val minRightPadding = 20
@@ -448,7 +447,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
left = minLeftPadding,
top = 0,
right = dcBoundsPortrait.left - dotWidth,
- bottom = sbHeightPortrait
+ bottom = sbHeightPortrait,
)
var bounds =
@@ -463,7 +462,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -475,7 +474,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
left = dcBoundsLandscape.height(),
top = 0,
right = screenBounds.height() - minRightPadding,
- bottom = sbHeightLandscape
+ bottom = sbHeightLandscape,
)
bounds =
@@ -490,7 +489,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -502,7 +501,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
left = minLeftPadding,
top = 0,
right = screenBounds.width() - minRightPadding,
- bottom = sbHeightPortrait
+ bottom = sbHeightPortrait,
)
bounds =
@@ -517,7 +516,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -529,7 +528,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
left = minLeftPadding,
top = 0,
right = screenBounds.height() - minRightPadding,
- bottom = sbHeightLandscape
+ bottom = sbHeightLandscape,
)
bounds =
@@ -544,7 +543,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -584,7 +583,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -595,7 +594,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
protectionBounds.bottom,
0,
screenBounds.height() - minRightPadding,
- sbHeightLandscape
+ sbHeightLandscape,
)
bounds =
@@ -610,7 +609,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -633,7 +632,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -645,7 +644,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
0,
screenBounds.height() - protectionBounds.bottom - dotWidth,
- sbHeightLandscape
+ sbHeightLandscape,
)
bounds =
@@ -660,7 +659,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -682,7 +681,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl = false,
dotWidth = 10,
bottomAlignedMargin = BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight = 15
+ statusBarContentHeight = 15,
)
assertThat(bounds.top).isEqualTo(0)
@@ -704,7 +703,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl = false,
dotWidth = 10,
bottomAlignedMargin = 5,
- statusBarContentHeight = 15
+ statusBarContentHeight = 15,
)
// Content in the status bar is centered vertically. To achieve the bottom margin we want,
@@ -756,7 +755,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -777,7 +776,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -798,7 +797,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -809,7 +808,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
minLeftPadding,
0,
screenBounds.height() - dcBounds.height() - dotWidth,
- sbHeightLandscape
+ sbHeightLandscape,
)
bounds =
@@ -824,7 +823,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -860,7 +859,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -880,7 +879,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -900,7 +899,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -920,7 +919,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -958,7 +957,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight
+ statusBarContentHeight,
)
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -968,12 +967,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
fun testDisplayChanged_returnsUpdatedInsets() {
// GIVEN: get insets on the first display and switch to the second display
val provider =
- StatusBarContentInsetsProvider(
+ StatusBarContentInsetsProviderImpl(
contextMock,
configurationController,
mock<DumpManager>(),
mock<CommandRegistry>(),
- mock<SysUICutoutProvider>()
+ mock<SysUICutoutProvider>(),
)
configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
@@ -993,12 +992,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
// GIVEN: get insets on the first display, switch to the second display,
// get insets and switch back
val provider =
- StatusBarContentInsetsProvider(
+ StatusBarContentInsetsProviderImpl(
contextMock,
configurationController,
mock<DumpManager>(),
mock<CommandRegistry>(),
- mock<SysUICutoutProvider>()
+ mock<SysUICutoutProvider>(),
)
configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
@@ -1024,12 +1023,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100)
configurationController.onConfigurationChanged(configuration)
val provider =
- StatusBarContentInsetsProvider(
+ StatusBarContentInsetsProviderImpl(
contextMock,
configurationController,
mock<DumpManager>(),
mock<CommandRegistry>(),
- mock<SysUICutoutProvider>()
+ mock<SysUICutoutProvider>(),
)
val listener =
object : StatusBarContentInsetsChangedListener {
@@ -1053,12 +1052,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
fun onDensityOrFontScaleChanged_listenerNotified() {
configuration.densityDpi = 12
val provider =
- StatusBarContentInsetsProvider(
+ StatusBarContentInsetsProviderImpl(
contextMock,
configurationController,
mock<DumpManager>(),
mock<CommandRegistry>(),
- mock<SysUICutoutProvider>()
+ mock<SysUICutoutProvider>(),
)
val listener =
object : StatusBarContentInsetsChangedListener {
@@ -1081,12 +1080,12 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
@Test
fun onThemeChanged_listenerNotified() {
val provider =
- StatusBarContentInsetsProvider(
+ StatusBarContentInsetsProviderImpl(
contextMock,
configurationController,
mock<DumpManager>(),
mock<CommandRegistry>(),
- mock<SysUICutoutProvider>()
+ mock<SysUICutoutProvider>(),
)
val listener =
object : StatusBarContentInsetsChangedListener {
@@ -1108,13 +1107,13 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
expected: Rect,
actual: Rect,
@Rotation currentRotation: Int,
- @Rotation targetRotation: Int
+ @Rotation targetRotation: Int,
) {
assertTrue(
"Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" +
" targetRotation=${RotationUtils.toString(targetRotation)}" +
" expected=$expected actual=$actual",
- expected.equals(actual)
+ expected.equals(actual),
)
}
@@ -1126,7 +1125,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
left: Rect = Rect(),
top: Rect = Rect(),
right: Rect = Rect(),
- bottom: Rect = Rect()
+ bottom: Rect = Rect(),
) {
whenever(dc.boundingRects)
.thenReturn(listOf(left, top, right, bottom).filter { !it.isEmpty })
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 1d74331e429b..94753f7e5203 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -34,7 +34,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,9 +42,7 @@ import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.service.trust.TrustAgentService;
import android.testing.TestableLooper;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
@@ -67,7 +64,6 @@ import com.android.keyguard.KeyguardMessageAreaController;
import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.TrustGrantFlags;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
@@ -580,22 +576,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
- @DisableSceneContainer
- @DisableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void testShowAlternateBouncer_unlockingWithBiometricAllowed() {
- // GIVEN will show alternate bouncer
- when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(false);
- when(mAlternateBouncerInteractor.show()).thenReturn(true);
-
- // WHEN showGenericBouncer is called
- mStatusBarKeyguardViewManager.showBouncer(true);
-
- // THEN alt auth bouncer is shown
- verify(mAlternateBouncerInteractor).show();
- verify(mPrimaryBouncerInteractor, never()).show(anyBoolean());
- }
-
- @Test
public void testUpdateResources_delegatesToBouncer() {
mStatusBarKeyguardViewManager.updateResources();
@@ -841,145 +821,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
- @EnableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void handleDispatchTouchEvent_alternateBouncerViewFlagEnabled() {
- mStatusBarKeyguardViewManager.addCallback(mCallback);
-
- // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
- // THEN the touch is not acted upon
- verify(mCallback, never()).onTouch(any());
- }
-
- @Test
- @EnableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void onInterceptTouch_alternateBouncerViewFlagEnabled() {
- // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
- // THEN the touch is not intercepted
- assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
- ));
- }
-
- @Test
- public void handleDispatchTouchEvent_alternateBouncerNotVisible() {
- mStatusBarKeyguardViewManager.addCallback(mCallback);
-
- // GIVEN the alternate bouncer is visible
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
-
- // THEN handleDispatchTouchEvent doesn't use the touches
- assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
- ));
- assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
- ));
- assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
- ));
-
- // THEN the touch is not acted upon
- verify(mCallback, never()).onTouch(any());
- }
-
- @Test
- @DisableSceneContainer
- @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void handleDispatchTouchEvent_shouldInterceptTouchAndHandleTouch() {
- mStatusBarKeyguardViewManager.addCallback(mCallback);
-
- // GIVEN the alternate bouncer is visible
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
- // GIVEN all touches are NOT the udfps overlay
- when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
-
- // THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent
- // to its child views (handleDispatchTouchEvent returns true)
- assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
- ));
- assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
- ));
- assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
- ));
-
- // THEN the touch is acted upon once for each dispatchTOuchEvent call
- verify(mCallback, times(3)).onTouch(any());
- }
-
- @Test
- @DisableSceneContainer
- @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void handleDispatchTouchEvent_shouldInterceptTouchButNotHandleTouch() {
- mStatusBarKeyguardViewManager.addCallback(mCallback);
-
- // GIVEN the alternate bouncer is visible
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
- // GIVEN all touches are within the udfps overlay
- when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(true);
-
- // THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent
- // to its child views (handleDispatchTouchEvent returns true)
- assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
- ));
- assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
- ));
- assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
- ));
-
- // THEN the touch is NOT acted upon at the moment
- verify(mCallback, never()).onTouch(any());
- }
-
- @Test
- @DisableSceneContainer
- public void shouldInterceptTouch_alternateBouncerNotVisible() {
- // GIVEN the alternate bouncer is not visible
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
-
- // THEN no motion events are intercepted
- assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
- ));
- assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
- ));
- assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
- ));
- }
-
- @Test
- @DisableSceneContainer
- @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void shouldInterceptTouch_alternateBouncerVisible() {
- // GIVEN the alternate bouncer is visible
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
- // THEN all motion events are intercepted
- assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
- ));
- assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
- ));
- assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
- ));
- }
-
- @Test
public void alternateBouncerToShowPrimaryBouncer_updatesScrimControllerOnce() {
// GIVEN the alternate bouncer has shown and calls to hide() will result in successfully
// hiding it
@@ -997,106 +838,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
@DisableSceneContainer
- @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void alternateBouncerOnTouch_actionDownThenUp_noMinTimeShown_noHideAltBouncer() {
- reset(mAlternateBouncerInteractor);
-
- // GIVEN the alternate bouncer has shown for a minimum amount of time
- when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(false);
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
-
- // WHEN ACTION_DOWN and ACTION_UP touch event comes
- boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
- when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true);
- boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0));
-
- // THEN the touches are handled (doesn't let touches through to underlying views)
- assertTrue(touchHandledDown);
- assertTrue(touchHandledUp);
-
- // THEN alternate bouncer does NOT attempt to hide since min showing time wasn't met
- verify(mAlternateBouncerInteractor, never()).hide();
- }
-
- @Test
- @DisableSceneContainer
- @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void alternateBouncerOnTouch_actionDownThenUp_handlesTouch_hidesAltBouncer() {
- reset(mAlternateBouncerInteractor);
-
- // GIVEN the alternate bouncer has shown for a minimum amount of time
- when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true);
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
-
- // WHEN ACTION_DOWN and ACTION_UP touch event comes
- boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0));
- when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true);
- boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0));
-
- // THEN the touches are handled
- assertTrue(touchHandledDown);
- assertTrue(touchHandledUp);
-
- // THEN alternate bouncer attempts to hide
- verify(mAlternateBouncerInteractor).hide();
- }
-
- @Test
- @DisableSceneContainer
- public void alternateBouncerOnTouch_actionUp_doesNotHideAlternateBouncer() {
- reset(mAlternateBouncerInteractor);
-
- // GIVEN the alternate bouncer has shown for a minimum amount of time
- when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true);
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false);
-
- // WHEN only ACTION_UP touch event comes
- mStatusBarKeyguardViewManager.onTouch(
- MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0));
-
- // THEN the alternateBouncer doesn't hide
- verify(mAlternateBouncerInteractor, never()).hide();
- }
-
- @Test
- @DisableSceneContainer
- @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void onTrustChanged_hideAlternateBouncerAndClearMessageArea() {
- // GIVEN keyguard update monitor callback is registered
- verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture());
-
- reset(mKeyguardUpdateMonitor);
- reset(mKeyguardMessageAreaController);
-
- // GIVEN alternate bouncer state = not visible
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
-
- // WHEN the device is trusted by active unlock
- mKeyguardUpdateMonitorCallback.getValue().onTrustGrantedForCurrentUser(
- true,
- true,
- new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD
- | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE),
- null
- );
-
- // THEN the false visibility state is propagated to the keyguardUpdateMonitor
- verify(mKeyguardUpdateMonitor).setAlternateBouncerShowing(eq(false));
-
- // THEN message area visibility updated to FALSE with empty message
- verify(mKeyguardMessageAreaController).setIsVisible(eq(false));
- verify(mKeyguardMessageAreaController).setMessage(eq(""));
- }
-
- @Test
- @DisableSceneContainer
@DisableFlags(Flags.FLAG_SIM_PIN_RACE_CONDITION_ON_RESTART)
public void testShowBouncerOrKeyguard_needsFullScreen() {
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt
index 40c3f221e2df..29e9ba752b36 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt
@@ -61,6 +61,26 @@ class BackGestureRecognizerTest : SysuiTestCase() {
}
@Test
+ fun triggersProgressRelativeToDistance() {
+ assertProgressWhileMovingFingers(deltaX = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f)
+ assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE / 2, expectedProgress = 0.5f)
+ assertProgressWhileMovingFingers(deltaX = -SWIPE_DISTANCE, expectedProgress = 1f)
+ assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE, expectedProgress = 1f)
+ }
+
+ private fun assertProgressWhileMovingFingers(deltaX: Float, expectedProgress: Float) {
+ assertStateAfterEvents(
+ events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) },
+ expectedState = InProgress(progress = expectedProgress),
+ )
+ }
+
+ @Test
+ fun triggeredProgressIsNoBiggerThanOne() {
+ assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE * 2, expectedProgress = 1f)
+ }
+
+ @Test
fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
assertStateAfterEvents(
events = ThreeFingerGesture.swipeLeft(distancePx = SWIPE_DISTANCE / 2),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt
index 8406d3b99bac..ff0cec5e06e9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt
@@ -104,7 +104,8 @@ class EasterEggGestureTest : SysuiTestCase() {
}
private fun assertStateAfterTwoFingerGesture(gesturePath: List<Point>, wasTriggered: Boolean) {
- val events = TwoFingerGesture.createEvents { gesturePath.forEach { (x, y) -> move(x, y) } }
+ val events =
+ TwoFingerGesture.eventsForFullGesture { gesturePath.forEach { (x, y) -> move(x, y) } }
assertStateAfterEvents(events = events, wasTriggered = wasTriggered)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
index 043b77577978..7d3ed92cecc6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt
@@ -56,6 +56,27 @@ class HomeGestureRecognizerTest : SysuiTestCase() {
}
@Test
+ fun triggersProgressRelativeToDistance() {
+ assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f)
+ assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE, expectedProgress = 1f)
+ }
+
+ private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) {
+ assertStateAfterEvents(
+ events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) },
+ expectedState = InProgress(progress = expectedProgress),
+ )
+ }
+
+ @Test
+ fun triggeredProgressIsBetweenZeroAndOne() {
+ // going in the wrong direction
+ assertProgressWhileMovingFingers(deltaY = SWIPE_DISTANCE / 2, expectedProgress = 0f)
+ // going further than required distance
+ assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f)
+ }
+
+ @Test
fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
assertStateAfterEvents(
events = ThreeFingerGesture.swipeUp(distancePx = SWIPE_DISTANCE / 2),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
index 7095a91a4e5d..c5c0d59ea48b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt
@@ -77,11 +77,32 @@ class RecentAppsGestureRecognizerTest : SysuiTestCase() {
fun triggersGestureProgressForThreeFingerGestureStarted() {
assertStateAfterEvents(
events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
- expectedState = InProgress(),
+ expectedState = InProgress(progress = 0f),
)
}
@Test
+ fun triggersProgressRelativeToDistance() {
+ assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f)
+ assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE, expectedProgress = 1f)
+ }
+
+ private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) {
+ assertStateAfterEvents(
+ events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) },
+ expectedState = InProgress(progress = expectedProgress),
+ )
+ }
+
+ @Test
+ fun triggeredProgressIsBetweenZeroAndOne() {
+ // going in the wrong direction
+ assertProgressWhileMovingFingers(deltaY = SWIPE_DISTANCE / 2, expectedProgress = 0f)
+ // going further than required distance
+ assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f)
+ }
+
+ @Test
fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
assertStateAfterEvents(
events = ThreeFingerGesture.swipeUp(distancePx = SWIPE_DISTANCE / 2),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
index 296d4dce8ce4..42fe1e5d6bec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
@@ -25,11 +25,23 @@ import android.view.MotionEvent.ACTION_UP
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_X
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_Y
-/**
- * Interface for gesture builders which support creating list of [MotionEvent] for common swipe
- * gestures. For simple usage see swipe* methods or use [createEvents] for more specific scenarios.
- */
-interface MultiFingerGesture {
+/** Given gesture move events can build list of [MotionEvent]s included in that gesture */
+interface GestureEventsBuilder {
+ /**
+ * Creates full gesture including provided move events. This means returned events include DOWN,
+ * MOVE and UP. Note that move event's x and y is always relative to the starting one.
+ */
+ fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent>
+
+ /**
+ * Creates partial gesture including provided move events. This means returned events include
+ * DOWN and MOVE. Note that move event's x and y is always relative to the starting one.
+ */
+ fun eventsForGestureInProgress(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent>
+}
+
+/** Support creating list of [MotionEvent] for common swipe gestures. */
+interface MultiFingerGesture : GestureEventsBuilder {
companion object {
const val SWIPE_DISTANCE = 100f
@@ -37,27 +49,41 @@ interface MultiFingerGesture {
const val DEFAULT_Y = 500f
}
- fun swipeUp(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaY = -distancePx) }
-
- fun swipeDown(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaY = distancePx) }
+ fun swipeUp(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture {
+ move(deltaY = -distancePx)
+ }
- fun swipeRight(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaX = distancePx) }
+ fun swipeDown(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture {
+ move(deltaY = distancePx)
+ }
- fun swipeLeft(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaX = -distancePx) }
+ fun swipeRight(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture {
+ move(deltaX = distancePx)
+ }
- /**
- * Creates gesture with provided move events. Note that move event's x and y is always relative
- * to the starting one
- */
- fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent>
+ fun swipeLeft(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture {
+ move(deltaX = -distancePx)
+ }
}
object ThreeFingerGesture : MultiFingerGesture {
- override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> {
- return touchpadGesture(
+
+ private val moveEventsBuilder = MoveEventsBuilder(::threeFingerEvent)
+
+ override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> {
+ return buildGesture(
+ startEvents = { x, y -> startEvents(x, y) },
+ moveEvents = moveEventsBuilder.getEvents(moveEvents),
+ endEvents = { x, y -> endEvents(x, y) },
+ )
+ }
+
+ override fun eventsForGestureInProgress(
+ moveEvents: MoveEventsBuilder.() -> Unit
+ ): List<MotionEvent> {
+ return buildGesture(
startEvents = { x, y -> startEvents(x, y) },
- moveEvents = GestureBuilder(::threeFingerEvent).apply { moveEvents() }.events,
- endEvents = { x, y -> endEvents(x, y) }
+ moveEvents = moveEventsBuilder.getEvents(moveEvents),
)
}
@@ -65,7 +91,7 @@ object ThreeFingerGesture : MultiFingerGesture {
return listOf(
threeFingerEvent(ACTION_DOWN, x, y),
threeFingerEvent(ACTION_POINTER_DOWN, x, y),
- threeFingerEvent(ACTION_POINTER_DOWN, x, y)
+ threeFingerEvent(ACTION_POINTER_DOWN, x, y),
)
}
@@ -73,32 +99,43 @@ object ThreeFingerGesture : MultiFingerGesture {
return listOf(
threeFingerEvent(ACTION_POINTER_UP, x, y),
threeFingerEvent(ACTION_POINTER_UP, x, y),
- threeFingerEvent(ACTION_UP, x, y)
+ threeFingerEvent(ACTION_UP, x, y),
)
}
private fun threeFingerEvent(
action: Int,
x: Float = DEFAULT_X,
- y: Float = DEFAULT_Y
+ y: Float = DEFAULT_Y,
): MotionEvent {
return touchpadEvent(
action = action,
x = x,
y = y,
classification = MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE,
- axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f)
+ axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f),
)
}
}
object FourFingerGesture : MultiFingerGesture {
- override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> {
- return touchpadGesture(
+ private val moveEventsBuilder = MoveEventsBuilder(::fourFingerEvent)
+
+ override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> {
+ return buildGesture(
+ startEvents = { x, y -> startEvents(x, y) },
+ moveEvents = moveEventsBuilder.getEvents(moveEvents),
+ endEvents = { x, y -> endEvents(x, y) },
+ )
+ }
+
+ override fun eventsForGestureInProgress(
+ moveEvents: MoveEventsBuilder.() -> Unit
+ ): List<MotionEvent> {
+ return buildGesture(
startEvents = { x, y -> startEvents(x, y) },
- moveEvents = GestureBuilder(::fourFingerEvent).apply { moveEvents() }.events,
- endEvents = { x, y -> endEvents(x, y) }
+ moveEvents = moveEventsBuilder.getEvents(moveEvents),
)
}
@@ -107,7 +144,7 @@ object FourFingerGesture : MultiFingerGesture {
fourFingerEvent(ACTION_DOWN, x, y),
fourFingerEvent(ACTION_POINTER_DOWN, x, y),
fourFingerEvent(ACTION_POINTER_DOWN, x, y),
- fourFingerEvent(ACTION_POINTER_DOWN, x, y)
+ fourFingerEvent(ACTION_POINTER_DOWN, x, y),
)
}
@@ -116,61 +153,74 @@ object FourFingerGesture : MultiFingerGesture {
fourFingerEvent(ACTION_POINTER_UP, x, y),
fourFingerEvent(ACTION_POINTER_UP, x, y),
fourFingerEvent(ACTION_POINTER_UP, x, y),
- fourFingerEvent(ACTION_UP, x, y)
+ fourFingerEvent(ACTION_UP, x, y),
)
}
private fun fourFingerEvent(
action: Int,
x: Float = DEFAULT_X,
- y: Float = DEFAULT_Y
+ y: Float = DEFAULT_Y,
): MotionEvent {
return touchpadEvent(
action = action,
x = x,
y = y,
classification = MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE,
- axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 4f)
+ axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 4f),
)
}
}
object TwoFingerGesture : MultiFingerGesture {
- override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> {
- return touchpadGesture(
- startEvents = { x, y -> listOf(twoFingerEvent(ACTION_DOWN, x, y)) },
- moveEvents = GestureBuilder(::twoFingerEvent).apply { moveEvents() }.events,
- endEvents = { x, y -> listOf(twoFingerEvent(ACTION_UP, x, y)) }
+ private val moveEventsBuilder = MoveEventsBuilder(::twoFingerEvent)
+
+ override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> {
+ return buildGesture(
+ startEvents = { x, y -> startEvents(x, y) },
+ moveEvents = moveEventsBuilder.getEvents(moveEvents),
+ endEvents = { x, y -> listOf(twoFingerEvent(ACTION_UP, x, y)) },
+ )
+ }
+
+ override fun eventsForGestureInProgress(
+ moveEvents: MoveEventsBuilder.() -> Unit
+ ): List<MotionEvent> {
+ return buildGesture(
+ startEvents = { x, y -> startEvents(x, y) },
+ moveEvents = moveEventsBuilder.getEvents(moveEvents),
)
}
+ private fun startEvents(x: Float, y: Float) = listOf(twoFingerEvent(ACTION_DOWN, x, y))
+
private fun twoFingerEvent(
action: Int,
x: Float = DEFAULT_X,
- y: Float = DEFAULT_Y
+ y: Float = DEFAULT_Y,
): MotionEvent {
return touchpadEvent(
action = action,
x = x,
y = y,
classification = MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE,
- axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 2f)
+ axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 2f),
)
}
}
-private fun touchpadGesture(
+private fun buildGesture(
startEvents: (Float, Float) -> List<MotionEvent>,
moveEvents: List<MotionEvent>,
- endEvents: (Float, Float) -> List<MotionEvent>
+ endEvents: (Float, Float) -> List<MotionEvent> = { _, _ -> emptyList() },
): List<MotionEvent> {
val lastX = moveEvents.last().x
val lastY = moveEvents.last().y
return startEvents(DEFAULT_X, DEFAULT_Y) + moveEvents + endEvents(lastX, lastY)
}
-class GestureBuilder internal constructor(val eventBuilder: (Int, Float, Float) -> MotionEvent) {
+class MoveEventsBuilder internal constructor(val eventBuilder: (Int, Float, Float) -> MotionEvent) {
val events = mutableListOf<MotionEvent>()
@@ -178,3 +228,11 @@ class GestureBuilder internal constructor(val eventBuilder: (Int, Float, Float)
events.add(eventBuilder(ACTION_MOVE, DEFAULT_X + deltaX, DEFAULT_Y + deltaY))
}
}
+
+private fun MoveEventsBuilder.getEvents(
+ moveEvents: MoveEventsBuilder.() -> Unit
+): List<MotionEvent> {
+ events.clear()
+ this.moveEvents()
+ return events
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
index 13ebb42531b8..64136775b4eb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
@@ -54,7 +54,28 @@ class TouchpadGestureBuilderTest : SysuiTestCase() {
ACTION_MOVE,
ACTION_POINTER_UP,
ACTION_POINTER_UP,
- ACTION_UP
+ ACTION_UP,
+ )
+ .inOrder()
+ }
+
+ @Test
+ fun threeFingerGestureInProgressProducesCorrectEvents() {
+ val events =
+ ThreeFingerGesture.eventsForGestureInProgress {
+ move(deltaX = 10f)
+ move(deltaX = 20f)
+ }
+
+ val actions = events.map { it.actionMasked }
+ assertWithMessage("Events have expected action type")
+ .that(actions)
+ .containsExactly(
+ ACTION_DOWN,
+ ACTION_POINTER_DOWN,
+ ACTION_POINTER_DOWN,
+ ACTION_MOVE,
+ ACTION_MOVE,
)
.inOrder()
}
@@ -80,7 +101,7 @@ class TouchpadGestureBuilderTest : SysuiTestCase() {
ACTION_POINTER_UP,
ACTION_POINTER_UP,
ACTION_POINTER_UP,
- ACTION_UP
+ ACTION_UP,
)
.inOrder()
}
@@ -109,7 +130,7 @@ class TouchpadGestureBuilderTest : SysuiTestCase() {
@Test
fun gestureBuilderProducesCorrectEventCoordinates() {
val events =
- ThreeFingerGesture.createEvents {
+ ThreeFingerGesture.eventsForFullGesture {
move(deltaX = 50f)
move(deltaX = 100f)
}
@@ -127,7 +148,7 @@ class TouchpadGestureBuilderTest : SysuiTestCase() {
// up events
DEFAULT_X + 100f to DEFAULT_Y,
DEFAULT_X + 100f to DEFAULT_Y,
- DEFAULT_X + 100f to DEFAULT_Y
+ DEFAULT_X + 100f to DEFAULT_Y,
)
.inOrder()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
index a867eb38b44c..c302b40fc4d7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
@@ -85,7 +85,7 @@ class TouchpadGestureHandlerTest : SysuiTestCase() {
}
private fun backGestureEvents(): List<MotionEvent> {
- return ThreeFingerGesture.createEvents {
+ return ThreeFingerGesture.eventsForFullGesture {
move(deltaX = SWIPE_DISTANCE / 4)
move(deltaX = SWIPE_DISTANCE / 2)
move(deltaX = SWIPE_DISTANCE)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
index b8f581574848..a4b3916b7a55 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
@@ -35,7 +35,7 @@ class DrawableSizeTest : SysuiTestCase() {
val drawable =
BitmapDrawable(
resources,
- Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888)
+ Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888),
)
val result = DrawableSize.downscaleToSize(resources, drawable, 300, 300)
assertThat(result).isSameInstanceAs(drawable)
@@ -48,7 +48,7 @@ class DrawableSizeTest : SysuiTestCase() {
val drawable =
BitmapDrawable(
resources,
- Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888)
+ Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888),
)
val result = DrawableSize.downscaleToSize(resources, drawable, 75, 75)
@@ -64,4 +64,31 @@ class DrawableSizeTest : SysuiTestCase() {
val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1)
assertThat(result).isSameInstanceAs(drawable)
}
+
+ @Test
+ fun testDownscaleToSize_layerDrawable_allLayersSameType_resized() {
+ val drawable =
+ resources.getDrawable(
+ com.android.systemui.tests.R.drawable.layer_drawable_all_same_type,
+ resources.newTheme(),
+ )
+
+ val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1)
+
+ assertThat(result).isNotSameInstanceAs(drawable)
+ }
+
+ /** Regression test for b/244282477. */
+ @Test
+ fun testDownscaleToSize_layerDrawable_layersAreDifferentTypes_unchanged() {
+ val drawable =
+ resources.getDrawable(
+ com.android.systemui.tests.R.drawable.layer_drawable_different_types,
+ resources.newTheme(),
+ )
+
+ val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1)
+
+ assertThat(result).isSameInstanceAs(drawable)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt
new file mode 100644
index 000000000000..7361de7d21b0
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/AudioSlidersInteractorTest.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.systemui.volume.panel.component.volume.domain.interactor
+
+import android.media.AudioManager
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.data.repository.audioRepository
+import com.android.systemui.volume.panel.component.volume.domain.model.SliderType
+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
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class AudioSlidersInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AudioSlidersInteractor
+
+ @Before
+ fun setUp() =
+ with(kosmos) {
+ audioRepository.setMode(AudioManager.MODE_NORMAL)
+ underTest = audioSlidersInteractor
+ }
+
+ @Test
+ fun shouldAddAllStreams_notInCall() =
+ with(kosmos) {
+ testScope.runTest {
+ val sliders by collectLastValue(underTest.volumePanelSliders)
+ runCurrent()
+
+ assertThat(sliders).isEqualTo(
+ mutableListOf(
+ AudioManager.STREAM_MUSIC,
+ AudioManager.STREAM_VOICE_CALL,
+ AudioManager.STREAM_RING,
+ AudioManager.STREAM_NOTIFICATION,
+ AudioManager.STREAM_ALARM
+ ).map { SliderType.Stream(AudioStream(it)) })
+ }
+ }
+
+ @Test
+ fun shouldAddAllStreams_inCall() =
+ with(kosmos) {
+ testScope.runTest {
+ audioRepository.setMode(AudioManager.MODE_IN_CALL)
+
+ val sliders by collectLastValue(underTest.volumePanelSliders)
+ runCurrent()
+
+ // Call stream is before music stream while in call.
+ assertThat(sliders).isEqualTo(
+ mutableListOf(
+ AudioManager.STREAM_VOICE_CALL,
+ AudioManager.STREAM_MUSIC,
+ AudioManager.STREAM_RING,
+ AudioManager.STREAM_NOTIFICATION,
+ AudioManager.STREAM_ALARM
+ ).map { SliderType.Stream(AudioStream(it)) })
+ }
+ }
+}
diff --git a/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml
index 627b92b8a779..3c1668405909 100644
--- a/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml
+++ b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml
@@ -21,11 +21,8 @@
android:color="?android:attr/colorControlHighlight">
<item>
<shape android:shape="rectangle">
- <solid android:color="@android:color/transparent"/>
+ <solid android:color="?androidprv:attr/materialColorPrimaryContainer"/>
<corners android:radius="@dimen/hearing_devices_preset_spinner_background_radius"/>
- <stroke
- android:width="1dp"
- android:color="?androidprv:attr/textColorTertiary" />
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
index 4a7bef9f48b9..80692f9481b7 100644
--- a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
@@ -72,7 +72,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/device_function_barrier"
- app:layout_constraintBottom_toTopOf="@id/related_tools_scroll"
android:drawableStart="@drawable/ic_add"
android:drawablePadding="20dp"
android:drawableTint="?android:attr/textColorPrimary"
@@ -92,24 +91,16 @@
app:barrierDirection="bottom"
app:constraint_referenced_ids="device_function_barrier, pair_new_device_button" />
- <HorizontalScrollView
- android:id="@+id/related_tools_scroll"
+ <LinearLayout
+ android:id="@+id/related_tools_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/bluetooth_dialog_layout_margin"
android:layout_marginEnd="@dimen/bluetooth_dialog_layout_margin"
android:layout_marginTop="@dimen/hearing_devices_layout_margin"
- android:scrollbars="none"
- android:fillViewport="true"
+ android:orientation="horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/preset_spinner">
- <LinearLayout
- android:id="@+id/related_tools_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal">
- </LinearLayout>
- </HorizontalScrollView>
+ app:layout_constraintTop_toBottomOf="@id/device_barrier" />
</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hearing_tool_item.xml b/packages/SystemUI/res/layout/hearing_tool_item.xml
index ff2fbe070e0f..f5baf2aaf6dc 100644
--- a/packages/SystemUI/res/layout/hearing_tool_item.xml
+++ b/packages/SystemUI/res/layout/hearing_tool_item.xml
@@ -17,8 +17,8 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tool_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="top|center_horizontal"
android:focusable="true"
@@ -26,8 +26,10 @@
android:layout_weight="1">
<FrameLayout
android:id="@+id/icon_frame"
- android:layout_width="@dimen/hearing_devices_tool_icon_frame_width"
- android:layout_height="@dimen/hearing_devices_tool_icon_frame_height"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="20dp"
+ android:paddingBottom="20dp"
android:background="@drawable/qs_hearing_devices_related_tools_background"
android:focusable="false" >
<ImageView
diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
index 690a89a044b7..d0a1ce8ae629 100644
--- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -45,31 +45,26 @@
android:tint="?android:attr/colorPrimary"
/>
- <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text] will ever
- be shown at one time. -->
+ <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text,
+ ongoing_activity_chip_short_time_delta] will ever be shown at one time. -->
+
+ <!-- Shows a timer, like 00:01. -->
<com.android.systemui.statusbar.chips.ui.view.ChipChronometer
android:id="@+id/ongoing_activity_chip_time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:gravity="center|start"
- android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding"
- android:textAppearance="@android:style/TextAppearance.Material.Small"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textColor="?android:attr/colorPrimary"
+ style="@style/StatusBar.Chip.Text"
/>
- <!-- Used to show generic text in the chip instead of a timer. -->
+ <!-- Shows generic text. -->
<TextView
android:id="@+id/ongoing_activity_chip_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:gravity="center|start"
- android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding"
- android:textAppearance="@android:style/TextAppearance.Material.Small"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textColor="?android:attr/colorPrimary"
+ style="@style/StatusBar.Chip.Text"
+ android:visibility="gone"
+ />
+
+ <!-- Shows a time delta in short form, like "15min" or "1hr". -->
+ <android.widget.DateTimeView
+ android:id="@+id/ongoing_activity_chip_short_time_delta"
+ style="@style/StatusBar.Chip.Text"
android:visibility="gone"
/>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index e21466671363..77fbb642f664 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -128,12 +128,6 @@
<include layout="@layout/dock_info_bottom_area_overlay" />
- <com.android.keyguard.LockIconView
- android:id="@+id/lock_icon_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- </com.android.keyguard.LockIconView>
-
<include
layout="@layout/keyguard_bottom_area"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 22d34eb7b115..fbb07bed4b50 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -58,22 +58,22 @@
android:layout_height="match_parent"
android:visibility="invisible" />
- <!-- Shared container for the notification stack. Can be positioned by either
- the keyguard_root_view or notification_panel -->
- <com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
- android:id="@+id/shared_notification_container"
+ <!-- Root for all keyguard content. It was previously located within the shade. -->
+ <com.android.systemui.keyguard.ui.view.KeyguardRootView
+ android:id="@id/keyguard_root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
- android:clipToPadding="false"
/>
- <!-- Root for all keyguard content. It was previously located within the shade. -->
- <com.android.systemui.keyguard.ui.view.KeyguardRootView
- android:id="@id/keyguard_root_view"
+ <!-- Shared container for the notification stack. Can be positioned by either
+ the keyguard_root_view or notification_panel -->
+ <com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+ android:id="@+id/shared_notification_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
+ android:clipToPadding="false"
/>
<include layout="@layout/brightness_mirror_container" />
diff --git a/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml b/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml
deleted file mode 100644
index 4799f8c5b668..000000000000
--- a/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<com.android.systemui.biometrics.UdfpsFpmEmptyView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/udfps_animation_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <!-- The layout height/width are placeholders, which will be overwritten by
- FingerprintSensorPropertiesInternal. -->
- <View
- android:id="@+id/udfps_enroll_accessibility_view"
- android:layout_gravity="center"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:contentDescription="@string/accessibility_fingerprint_label"/>
-</com.android.systemui.biometrics.UdfpsFpmEmptyView>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
deleted file mode 100644
index 257d238f5c54..000000000000
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 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.systemui.biometrics.UdfpsView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:id="@+id/udfps_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- systemui:sensorTouchAreaCoefficient="1.0"
- android:contentDescription="@string/accessibility_fingerprint_label">
-
- <ViewStub
- android:id="@+id/animation_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
-</com.android.systemui.biometrics.UdfpsView>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index b5efeb5f6b3b..5d5b95546d0d 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -25,6 +25,12 @@
<integer name="quick_settings_num_columns">4</integer>
+ <!-- The number of rows in the paginated grid QuickSettings -->
+ <integer name="quick_settings_paginated_grid_num_rows">2</integer>
+
+ <!-- The number of rows in the paginated grid QuickQuickSettings -->
+ <integer name="quick_qs_paginated_grid_num_rows">1</integer>
+
<!-- The number of columns in the infinite grid QuickSettings -->
<integer name="quick_settings_infinite_grid_num_columns">8</integer>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index fc6d20e11d3b..c661846d025f 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -27,6 +27,12 @@
<!-- Whether to use the split 2-column notification shade -->
<bool name="config_use_split_notification_shade">true</bool>
+ <!-- The number of rows in the paginated grid QuickSettings -->
+ <integer name="quick_settings_paginated_grid_num_rows">3</integer>
+
+ <!-- The number of rows in the paginated grid QuickQuickSettings -->
+ <integer name="quick_qs_paginated_grid_num_rows">2</integer>
+
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">2</integer>
diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml
index 7daad1a43f73..f556b97eefc2 100644
--- a/packages/SystemUI/res/values-sw600dp-port/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/config.xml
@@ -21,6 +21,12 @@
<!-- The maximum number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">3</integer>
+ <!-- The number of rows in the paginated grid QuickSettings -->
+ <integer name="quick_settings_paginated_grid_num_rows">3</integer>
+
+ <!-- The number of rows in the paginated grid QuickQuickSettings -->
+ <integer name="quick_qs_paginated_grid_num_rows">2</integer>
+
<!-- The number of columns in the QuickSettings -->
<integer name="quick_settings_num_columns">3</integer>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 6f94f9e2a216..7d840cfe949a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -70,6 +70,12 @@
<!-- The number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">4</integer>
+ <!-- The number of rows in the paginated grid QuickSettings -->
+ <integer name="quick_settings_paginated_grid_num_rows">4</integer>
+
+ <!-- The number of rows in the paginated grid QuickQuickSettings -->
+ <integer name="quick_qs_paginated_grid_num_rows">2</integer>
+
<!-- The number of columns in the infinite grid QuickSettings -->
<integer name="quick_settings_infinite_grid_num_columns">4</integer>
@@ -128,17 +134,19 @@
<!-- Use collapsed layout for media player in landscape QQS -->
<bool name="config_quickSettingsMediaLandscapeCollapsed">true</bool>
- <!-- For hearing devices related tool list. Need to be in ComponentName format (package/class).
- Should be activity to be launched.
- Already contains tool that holds intent: "com.android.settings.action.live_caption".
- Maximum number is 3. -->
+ <!-- Hearing devices related tool list. Each entry must be in ComponentName format
+ (package/class), indicating the specific application component to launch.
+ Already contains tool that handles intent: "com.android.settings.action.live_caption" by
+ default. You can add up to 2 additional related tools. -->
<string-array name="config_quickSettingsHearingDevicesRelatedToolName" translatable="false">
</string-array>
- <!-- The drawable resource names. If provided, it will replace the corresponding icons in
- config_quickSettingsHearingDevicesRelatedToolName. Can be empty to use original icons.
- Already contains tool that holds intent: "com.android.settings.action.live_caption".
- Maximum number is 3. -->
+ <!-- Hearing devices related tool icon list. Provide drawable resource names in the same order
+ as the component names in config_quickSettingsHearingDevicesRelatedToolName. The icons
+ should be monochrome and will be tinted according to the system's material color. Ensure
+ the number of icon resources matches the number of components specified in
+ config_quickSettingsHearingDevicesRelatedToolName. If this array is empty or the sizes
+ don't match, the original application icons will be used. -->
<string-array name="config_quickSettingsHearingDevicesRelatedToolIcon" translatable="false">
</string-array>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6c8a7403953e..209a971814f4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1798,8 +1798,6 @@
<dimen name="hearing_devices_preset_spinner_text_padding_vertical">15dp</dimen>
<dimen name="hearing_devices_preset_spinner_arrow_size">24dp</dimen>
<dimen name="hearing_devices_preset_spinner_background_radius">28dp</dimen>
- <dimen name="hearing_devices_tool_icon_frame_width">94dp</dimen>
- <dimen name="hearing_devices_tool_icon_frame_height">64dp</dimen>
<dimen name="hearing_devices_tool_icon_size">28dp</dimen>
<!-- Height percentage of the parent container occupied by the communal view -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e1f25f99cc98..7cebac2feaba 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -70,6 +70,20 @@
<item name="android:fontWeight">700</item>
</style>
+ <style name="StatusBar" />
+ <style name="StatusBar.Chip" />
+
+ <style name="StatusBar.Chip.Text">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:gravity">center|start</item>
+ <item name="android:paddingStart">@dimen/ongoing_activity_chip_icon_text_padding</item>
+ <item name="android:textAppearance">@android:style/TextAppearance.Material.Small</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">?android:attr/colorPrimary</item>
+ </style>
+
<style name="Chipbar" />
<style name="Chipbar.Text" parent="@*android:style/TextAppearance.DeviceDefault.Notification.Title">
@@ -677,10 +691,12 @@
<style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings">
<item name="android:windowActionBar">false</item>
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
<item name="preferenceTheme">@style/TunerPreferenceTheme</item>
</style>
<style name="TunerPreferenceTheme" parent="@style/PreferenceThemeOverlay.SettingsBase">
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
<style name="TextAppearance.NotificationInfo.Confirmation">
diff --git a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt b/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
index b792db354b36..306d68217e50 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
@@ -17,6 +17,7 @@
package com.android.keyguard
import android.view.MotionEvent
+import android.view.View
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.res.R
@@ -34,11 +35,10 @@ import javax.inject.Inject
@SysUISingleton
class EmptyLockIconViewController
@Inject
-constructor(
- private val keyguardRootView: Lazy<KeyguardRootView>,
-) : LockIconViewController {
+constructor(private val keyguardRootView: Lazy<KeyguardRootView>) : LockIconViewController {
private val deviceEntryIconViewId = R.id.device_entry_icon_view
- override fun setLockIconView(lockIconView: LockIconView) {
+
+ override fun setLockIconView(lockIconView: View) {
// no-op
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
deleted file mode 100644
index 03b13fe47c10..000000000000
--- a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
+++ /dev/null
@@ -1,843 +0,0 @@
-/*
- * Copyright (C) 2020 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.keyguard;
-
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
-
-import static com.android.keyguard.LockIconView.ICON_FINGERPRINT;
-import static com.android.keyguard.LockIconView.ICON_LOCK;
-import static com.android.keyguard.LockIconView.ICON_UNLOCK;
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
-import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.VibrationAttributes;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.HapticFeedbackConstants;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-
-import com.android.systemui.Dumpable;
-import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.biometrics.AuthRippleController;
-import com.android.systemui.biometrics.UdfpsController;
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.shared.model.KeyguardState;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import dagger.Lazy;
-
-import kotlinx.coroutines.ExperimentalCoroutinesApi;
-
-import java.io.PrintWriter;
-import java.util.Objects;
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-
-/**
- * Controls when to show the LockIcon affordance (lock/unlocked icon or circle) on lock screen.
- *
- * For devices with UDFPS, the lock icon will show at the sensor location. Else, the lock
- * icon will show a set distance from the bottom of the device.
- */
-@SysUISingleton
-public class LegacyLockIconViewController implements Dumpable, LockIconViewController {
- private static final String TAG = "LockIconViewController";
- private static final float sDefaultDensity =
- (float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT;
- private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36);
- private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
-
- private static final long FADE_OUT_DURATION_MS = 250L;
-
- private final long mLongPressTimeout;
- @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @NonNull private final KeyguardViewController mKeyguardViewController;
- @NonNull private final StatusBarStateController mStatusBarStateController;
- @NonNull private final KeyguardStateController mKeyguardStateController;
- @NonNull private final FalsingManager mFalsingManager;
- @NonNull private final AuthController mAuthController;
- @NonNull private final AccessibilityManager mAccessibilityManager;
- @NonNull private final ConfigurationController mConfigurationController;
- @NonNull private final DelayableExecutor mExecutor;
- private boolean mUdfpsEnrolled;
- private Resources mResources;
- private Context mContext;
- @NonNull private CharSequence mUnlockedLabel;
- @NonNull private CharSequence mLockedLabel;
- @NonNull private final VibratorHelper mVibrator;
- @Nullable private final AuthRippleController mAuthRippleController;
- @NonNull private final FeatureFlags mFeatureFlags;
- @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
- @NonNull private final KeyguardTransitionInteractor mTransitionInteractor;
- @NonNull private final KeyguardInteractor mKeyguardInteractor;
- @NonNull private final View.AccessibilityDelegate mAccessibilityDelegate;
- @NonNull private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractor;
-
- // Tracks the velocity of a touch to help filter out the touches that move too fast.
- private VelocityTracker mVelocityTracker;
- // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active.
- private int mActivePointerId = -1;
-
- private boolean mIsDozing;
- private boolean mIsActiveDreamLockscreenHosted;
- private boolean mIsBouncerShowing;
- private boolean mRunningFPS;
- private boolean mCanDismissLockScreen;
- private int mStatusBarState;
- private boolean mIsKeyguardShowing;
- private Runnable mLongPressCancelRunnable;
-
- private boolean mUdfpsSupported;
- private float mHeightPixels;
- private float mWidthPixels;
- private int mBottomPaddingPx;
- private int mDefaultPaddingPx;
-
- private boolean mShowUnlockIcon;
- private boolean mShowLockIcon;
-
- // for udfps when strong auth is required or unlocked on AOD
- private boolean mShowAodLockIcon;
- private boolean mShowAodUnlockedIcon;
- private final int mMaxBurnInOffsetX;
- private final int mMaxBurnInOffsetY;
- private float mInterpolatedDarkAmount;
-
- private boolean mDownDetected;
- private final Rect mSensorTouchLocation = new Rect();
- private LockIconView mView;
-
- @VisibleForTesting
- final Consumer<Float> mDozeTransitionCallback = (Float value) -> {
- mInterpolatedDarkAmount = value;
- mView.setDozeAmount(value);
- updateBurnInOffsets();
- };
-
- @VisibleForTesting
- final Consumer<Boolean> mIsDozingCallback = (Boolean isDozing) -> {
- mIsDozing = isDozing;
- updateBurnInOffsets();
- updateVisibility();
- };
-
- @VisibleForTesting
- final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback =
- (Boolean isLockscreenHosted) -> {
- mIsActiveDreamLockscreenHosted = isLockscreenHosted;
- updateVisibility();
- };
-
- @Inject
- public LegacyLockIconViewController(
- @NonNull StatusBarStateController statusBarStateController,
- @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
- @NonNull KeyguardViewController keyguardViewController,
- @NonNull KeyguardStateController keyguardStateController,
- @NonNull FalsingManager falsingManager,
- @NonNull AuthController authController,
- @NonNull DumpManager dumpManager,
- @NonNull AccessibilityManager accessibilityManager,
- @NonNull ConfigurationController configurationController,
- @NonNull @Main DelayableExecutor executor,
- @NonNull VibratorHelper vibrator,
- @Nullable AuthRippleController authRippleController,
- @NonNull @Main Resources resources,
- @NonNull KeyguardTransitionInteractor transitionInteractor,
- @NonNull KeyguardInteractor keyguardInteractor,
- @NonNull FeatureFlags featureFlags,
- PrimaryBouncerInteractor primaryBouncerInteractor,
- Context context,
- Lazy<DeviceEntryInteractor> deviceEntryInteractor
- ) {
- mStatusBarStateController = statusBarStateController;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mAuthController = authController;
- mKeyguardViewController = keyguardViewController;
- mKeyguardStateController = keyguardStateController;
- mFalsingManager = falsingManager;
- mAccessibilityManager = accessibilityManager;
- mConfigurationController = configurationController;
- mExecutor = executor;
- mVibrator = vibrator;
- mAuthRippleController = authRippleController;
- mTransitionInteractor = transitionInteractor;
- mKeyguardInteractor = keyguardInteractor;
- mFeatureFlags = featureFlags;
- mPrimaryBouncerInteractor = primaryBouncerInteractor;
-
- mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
- mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
- mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button);
- mLockedLabel = resources.getString(R.string.accessibility_lock_icon);
- mLongPressTimeout = resources.getInteger(R.integer.config_lockIconLongPress);
- dumpManager.registerDumpable(TAG, this);
- mResources = resources;
- mContext = context;
- mDeviceEntryInteractor = deviceEntryInteractor;
-
- mAccessibilityDelegate = new View.AccessibilityDelegate() {
- private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint =
- new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfoCompat.ACTION_CLICK,
- mResources.getString(R.string.accessibility_authenticate_hint));
- private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityEnterHint =
- new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfoCompat.ACTION_CLICK,
- mResources.getString(R.string.accessibility_enter_hint));
- public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(v, info);
- if (isActionable()) {
- if (mShowLockIcon) {
- info.addAction(mAccessibilityAuthenticateHint);
- } else if (mShowUnlockIcon) {
- info.addAction(mAccessibilityEnterHint);
- }
- }
- }
- };
- }
-
- /** Sets the LockIconView to the controller and rebinds any that depend on it. */
- @SuppressLint("ClickableViewAccessibility")
- @Override
- public void setLockIconView(LockIconView lockIconView) {
- mView = lockIconView;
- mView.setAccessibilityDelegate(mAccessibilityDelegate);
-
- if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
- collectFlow(mView, mTransitionInteractor.transitionValue(KeyguardState.AOD),
- mDozeTransitionCallback);
- collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback);
- }
-
- if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) {
- collectFlow(mView, mKeyguardInteractor.isActiveDreamLockscreenHosted(),
- mIsActiveDreamLockscreenHostedCallback);
- }
-
- updateIsUdfpsEnrolled();
- updateConfiguration();
- updateKeyguardShowing();
-
- mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
- mIsDozing = mStatusBarStateController.isDozing();
- mInterpolatedDarkAmount = mStatusBarStateController.getDozeAmount();
- mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
- mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
- mStatusBarState = mStatusBarStateController.getState();
-
- updateColors();
- mDownDetected = false;
- updateBurnInOffsets();
- updateVisibility();
-
- updateAccessibility();
-
- lockIconView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View view) {
- registerCallbacks();
- }
-
- @Override
- public void onViewDetachedFromWindow(View view) {
- unregisterCallbacks();
- }
- });
-
- if (lockIconView.isAttachedToWindow()) {
- registerCallbacks();
- }
-
- lockIconView.setOnTouchListener((view, motionEvent) -> onTouchEvent(motionEvent));
- }
-
- private void registerCallbacks() {
- mConfigurationController.addCallback(mConfigurationListener);
- mAuthController.addCallback(mAuthControllerCallback);
- mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
- mStatusBarStateController.addCallback(mStatusBarStateListener);
- mKeyguardStateController.addCallback(mKeyguardStateCallback);
- mAccessibilityManager.addAccessibilityStateChangeListener(
- mAccessibilityStateChangeListener);
-
- }
-
- private void unregisterCallbacks() {
- mAuthController.removeCallback(mAuthControllerCallback);
- mConfigurationController.removeCallback(mConfigurationListener);
- mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
- mStatusBarStateController.removeCallback(mStatusBarStateListener);
- mKeyguardStateController.removeCallback(mKeyguardStateCallback);
- mAccessibilityManager.removeAccessibilityStateChangeListener(
- mAccessibilityStateChangeListener);
-
- }
-
- private void updateAccessibility() {
- if (mAccessibilityManager.isEnabled()) {
- mView.setOnClickListener(mA11yClickListener);
- } else {
- mView.setOnClickListener(null);
- }
- }
-
- @Override
- public float getTop() {
- return mView.getLocationTop();
- }
-
- @Override
- public float getBottom() {
- return mView.getLocationBottom();
- }
-
- private void updateVisibility() {
- if (!mIsKeyguardShowing && !mIsDozing) {
- mView.setVisibility(View.INVISIBLE);
- return;
- }
-
- if (mIsKeyguardShowing && mIsActiveDreamLockscreenHosted) {
- mView.setVisibility(View.INVISIBLE);
- return;
- }
-
- boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon
- && !mShowAodUnlockedIcon && !mShowAodLockIcon;
- mShowLockIcon = !mCanDismissLockScreen && isLockScreen()
- && (!mUdfpsEnrolled || !mRunningFPS);
- mShowUnlockIcon = mCanDismissLockScreen && isLockScreen();
- mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
- mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen;
-
- final CharSequence prevContentDescription = mView.getContentDescription();
- if (mShowLockIcon) {
- if (wasShowingFpIcon) {
- // fp icon was shown by UdfpsView, and now we still want to animate the transition
- // in this drawable
- mView.updateIcon(ICON_FINGERPRINT, false);
- }
- mView.updateIcon(ICON_LOCK, false);
- mView.setContentDescription(mLockedLabel);
- mView.setVisibility(View.VISIBLE);
- } else if (mShowUnlockIcon) {
- if (wasShowingFpIcon) {
- // fp icon was shown by UdfpsView, and now we still want to animate the transition
- // in this drawable
- mView.updateIcon(ICON_FINGERPRINT, false);
- }
- mView.updateIcon(ICON_UNLOCK, false);
- mView.setContentDescription(mUnlockedLabel);
- mView.setVisibility(View.VISIBLE);
- } else if (mShowAodUnlockedIcon) {
- mView.updateIcon(ICON_UNLOCK, true);
- mView.setContentDescription(mUnlockedLabel);
- mView.setVisibility(View.VISIBLE);
- } else if (mShowAodLockIcon) {
- mView.updateIcon(ICON_LOCK, true);
- mView.setContentDescription(mLockedLabel);
- mView.setVisibility(View.VISIBLE);
- } else {
- mView.clearIcon();
- mView.setVisibility(View.INVISIBLE);
- mView.setContentDescription(null);
- }
-
- boolean accessibilityEnabled =
- !mPrimaryBouncerInteractor.isAnimatingAway() && mView.isVisibleToUser();
- mView.setImportantForAccessibility(
- accessibilityEnabled ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
- : View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-
- if (!Objects.equals(prevContentDescription, mView.getContentDescription())
- && mView.getContentDescription() != null && accessibilityEnabled) {
- mView.announceForAccessibility(mView.getContentDescription());
- }
- }
-
- private boolean isLockScreen() {
- return !mIsDozing
- && !mIsBouncerShowing
- && mStatusBarState == StatusBarState.KEYGUARD;
- }
-
- private void updateKeyguardShowing() {
- mIsKeyguardShowing = mKeyguardStateController.isShowing()
- && !mKeyguardStateController.isKeyguardGoingAway();
- }
-
- private void updateColors() {
- mView.updateColorAndBackgroundVisibility();
- }
-
- private void updateConfiguration() {
- WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
- mWidthPixels = bounds.right;
- if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
- // Assumed to be initially neglected as there are no left or right insets in portrait
- // However, on landscape, these insets need to included when calculating the midpoint
- WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets();
- mWidthPixels -= insets.getSystemWindowInsetLeft() + insets.getSystemWindowInsetRight();
- }
- mHeightPixels = bounds.bottom;
- mBottomPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom);
- mDefaultPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_padding);
- mUnlockedLabel = mResources.getString(
- R.string.accessibility_unlock_button);
- mLockedLabel = mResources.getString(R.string.accessibility_lock_icon);
- updateLockIconLocation();
- }
-
- private void updateLockIconLocation() {
- final float scaleFactor = mAuthController.getScaleFactor();
- final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor);
- if (KeyguardBottomAreaRefactor.isEnabled() || MigrateClocksToBlueprint.isEnabled()) {
- // positioning in this case is handled by [DefaultDeviceEntrySection]
- mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding,
- scaledPadding);
- } else {
- if (mUdfpsSupported) {
- mView.setCenterLocation(mAuthController.getUdfpsLocation(),
- mAuthController.getUdfpsRadius(), scaledPadding);
- } else {
- mView.setCenterLocation(
- new Point((int) mWidthPixels / 2,
- (int) (mHeightPixels
- - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))),
- sLockIconRadiusPx * scaleFactor, scaledPadding);
- }
- }
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("mUdfpsSupported: " + mUdfpsSupported);
- pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled);
- pw.println("mIsKeyguardShowing: " + mIsKeyguardShowing);
- pw.println();
- pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
- pw.println(" mShowLockIcon: " + mShowLockIcon);
- pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon);
- pw.println();
- pw.println(" mIsDozing: " + mIsDozing);
- pw.println(" isFlagEnabled(DOZING_MIGRATION_1): "
- + mFeatureFlags.isEnabled(DOZING_MIGRATION_1));
- pw.println(" mIsBouncerShowing: " + mIsBouncerShowing);
- pw.println(" mRunningFPS: " + mRunningFPS);
- pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen);
- pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState));
- pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount);
- pw.println(" mSensorTouchLocation: " + mSensorTouchLocation);
- pw.println(" mDefaultPaddingPx: " + mDefaultPaddingPx);
- pw.println(" mIsActiveDreamLockscreenHosted: " + mIsActiveDreamLockscreenHosted);
-
- if (mView != null) {
- mView.dump(pw, args);
- }
- }
-
- /** Every minute, update the aod icon's burn in offset */
- @Override
- public void dozeTimeTick() {
- updateBurnInOffsets();
- }
-
- private void updateBurnInOffsets() {
- float offsetX = MathUtils.lerp(0f,
- getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
- - mMaxBurnInOffsetX, mInterpolatedDarkAmount);
- float offsetY = MathUtils.lerp(0f,
- getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
- - mMaxBurnInOffsetY, mInterpolatedDarkAmount);
-
- mView.setTranslationX(offsetX);
- mView.setTranslationY(offsetY);
- }
-
- private void updateIsUdfpsEnrolled() {
- boolean wasUdfpsSupported = mUdfpsSupported;
- boolean wasUdfpsEnrolled = mUdfpsEnrolled;
-
- mUdfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported();
- mView.setUseBackground(mUdfpsSupported);
-
- mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
- if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) {
- updateVisibility();
- }
- }
-
- private StatusBarStateController.StateListener mStatusBarStateListener =
- new StatusBarStateController.StateListener() {
- @Override
- public void onDozeAmountChanged(float linear, float eased) {
- if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
- mInterpolatedDarkAmount = eased;
- mView.setDozeAmount(eased);
- updateBurnInOffsets();
- }
- }
-
- @Override
- public void onDozingChanged(boolean isDozing) {
- if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
- mIsDozing = isDozing;
- updateBurnInOffsets();
- updateVisibility();
- }
- }
-
- @Override
- public void onStateChanged(int statusBarState) {
- mStatusBarState = statusBarState;
- updateVisibility();
- }
- };
-
- private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
- new KeyguardUpdateMonitorCallback() {
- @Override
- public void onKeyguardBouncerStateChanged(boolean bouncer) {
- mIsBouncerShowing = bouncer;
- updateVisibility();
- }
-
- @Override
- public void onBiometricRunningStateChanged(boolean running,
- BiometricSourceType biometricSourceType) {
- final boolean wasRunningFps = mRunningFPS;
-
- if (biometricSourceType == FINGERPRINT) {
- mRunningFPS = running;
- }
-
- if (wasRunningFps != mRunningFPS) {
- updateVisibility();
- }
- }
- };
-
- private final KeyguardStateController.Callback mKeyguardStateCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onUnlockedChanged() {
- mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
- updateKeyguardShowing();
- updateVisibility();
- }
-
- @Override
- public void onKeyguardShowingChanged() {
- // Reset values in case biometrics were removed (ie: pin/pattern/password => swipe).
- // If biometrics were removed, local vars mCanDismissLockScreen and
- // mUserUnlockedWithBiometric may not be updated.
- mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
-
- // reset mIsBouncerShowing state in case it was preemptively set
- // onLongPress
- mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
-
- updateKeyguardShowing();
- updateVisibility();
- }
-
- @Override
- public void onKeyguardFadingAwayChanged() {
- updateKeyguardShowing();
- updateVisibility();
- }
- };
-
- private final ConfigurationController.ConfigurationListener mConfigurationListener =
- new ConfigurationController.ConfigurationListener() {
- @Override
- public void onUiModeChanged() {
- updateColors();
- }
-
- @Override
- public void onThemeChanged() {
- updateColors();
- }
-
- @Override
- public void onConfigChanged(Configuration newConfig) {
- updateConfiguration();
- updateColors();
- }
- };
-
- /**
- * Handles the touch if {@link #isActionable()} is true.
- * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon
- * area for {@link #mLongPressTimeout} ms.
- *
- * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}.
- */
- private boolean onTouchEvent(MotionEvent event) {
- if (!actionableDownEventStartedOnView(event)) {
- cancelTouches();
- return false;
- }
-
- switch(event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_HOVER_ENTER:
- if (!mDownDetected && mAccessibilityManager.isTouchExplorationEnabled()) {
- vibrateOnTouchExploration();
- }
-
- // The pointer that causes ACTION_DOWN is always at index 0.
- // We need to persist its ID to track it during ACTION_MOVE that could include
- // data for many other pointers because of multi-touch support.
- mActivePointerId = event.getPointerId(0);
- if (mVelocityTracker == null) {
- // To simplify the lifecycle of the velocity tracker, make sure it's never null
- // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP.
- mVelocityTracker = VelocityTracker.obtain();
- } else {
- // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new
- // ACTION_DOWN, in that case we should just reuse the old instance.
- mVelocityTracker.clear();
- }
- mVelocityTracker.addMovement(event);
-
- mDownDetected = true;
- mLongPressCancelRunnable = mExecutor.executeDelayed(
- this::onLongPress, mLongPressTimeout);
- break;
- case MotionEvent.ACTION_MOVE:
- case MotionEvent.ACTION_HOVER_MOVE:
- mVelocityTracker.addMovement(event);
- // Compute pointer velocity in pixels per second.
- mVelocityTracker.computeCurrentVelocity(1000);
- float velocity = computePointerSpeed(mVelocityTracker,
- mActivePointerId);
- if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS
- && exceedsVelocityThreshold(velocity)) {
- Log.v(TAG, "lock icon long-press rescheduled due to "
- + "high pointer velocity=" + velocity);
- mLongPressCancelRunnable.run();
- mLongPressCancelRunnable = mExecutor.executeDelayed(
- this::onLongPress, mLongPressTimeout);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_HOVER_EXIT:
- cancelTouches();
- break;
- }
-
- return true;
- }
-
- /**
- * Calculate the pointer speed given a velocity tracker and the pointer id.
- * This assumes that the velocity tracker has already been passed all relevant motion events.
- */
- private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
- final float vx = tracker.getXVelocity(pointerId);
- final float vy = tracker.getYVelocity(pointerId);
- return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0));
- }
-
- /**
- * Whether the velocity exceeds the acceptable UDFPS debouncing threshold.
- */
- private static boolean exceedsVelocityThreshold(float velocity) {
- return velocity > 750f;
- }
-
- private boolean actionableDownEventStartedOnView(MotionEvent event) {
- if (!isActionable()) {
- return false;
- }
-
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- return true;
- }
-
- return mDownDetected;
- }
-
- @ExperimentalCoroutinesApi
- @VisibleForTesting
- protected void onLongPress() {
- cancelTouches();
- if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
- Log.v(TAG, "lock icon long-press rejected by the falsing manager.");
- return;
- }
-
- // pre-emptively set to true to hide view
- mIsBouncerShowing = true;
- if (!DeviceEntryUdfpsRefactor.isEnabled()
- && mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
- mAuthRippleController.showUnlockRipple(FINGERPRINT);
- }
- updateVisibility();
-
- // play device entry haptic (consistent with UDFPS controller longpress)
- vibrateOnLongPress();
-
- if (SceneContainerFlag.isEnabled()) {
- mDeviceEntryInteractor.get().attemptDeviceEntry();
- } else {
- mKeyguardViewController.showPrimaryBouncer(/* scrim */ true);
- }
- }
-
-
- private void cancelTouches() {
- mDownDetected = false;
- if (mLongPressCancelRunnable != null) {
- mLongPressCancelRunnable.run();
- }
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- }
-
- private boolean isActionable() {
- if (mIsBouncerShowing) {
- Log.v(TAG, "lock icon long-press ignored, bouncer already showing.");
- // a long press gestures from AOD may have already triggered the bouncer to show,
- // so this touch is no longer actionable
- return false;
- }
- return mUdfpsSupported || mShowUnlockIcon;
- }
-
- /**
- * Set the alpha of this view.
- */
- @Override
- public void setAlpha(float alpha) {
- mView.setAlpha(alpha);
- }
-
- private void updateUdfpsConfig() {
- // must be called from the main thread since it may update the views
- mExecutor.execute(() -> {
- updateIsUdfpsEnrolled();
- updateConfiguration();
- });
- }
-
- @VisibleForTesting
- void vibrateOnTouchExploration() {
- mVibrator.performHapticFeedback(
- mView,
- HapticFeedbackConstants.CONTEXT_CLICK
- );
- }
-
- @VisibleForTesting
- void vibrateOnLongPress() {
- mVibrator.performHapticFeedback(mView, UdfpsController.LONG_PRESS);
- }
-
- private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
- @Override
- public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) {
- if (modality == TYPE_FINGERPRINT) {
- updateUdfpsConfig();
- }
- }
-
- @Override
- public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
- if (modality == TYPE_FINGERPRINT) {
- updateUdfpsConfig();
- }
- }
-
- @Override
- public void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) {
- updateUdfpsConfig();
- }
- };
-
- /**
- * Whether the lock icon will handle a touch while dozing.
- */
- @Override
- public boolean willHandleTouchWhileDozing(MotionEvent event) {
- // is in lock icon area
- mView.getHitRect(mSensorTouchLocation);
- final boolean inLockIconArea =
- mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
- && mView.getVisibility() == View.VISIBLE;
-
- return inLockIconArea && actionableDownEventStartedOnView(event);
- }
-
- private final View.OnClickListener mA11yClickListener = v -> onLongPress();
-
- private final AccessibilityManager.AccessibilityStateChangeListener
- mAccessibilityStateChangeListener = enabled -> updateAccessibility();
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
deleted file mode 100644
index ff6a3d0cc6f0..000000000000
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (C) 2020 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.keyguard;
-
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-
-import com.android.internal.graphics.ColorUtils;
-import com.android.settingslib.Utils;
-import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
-
-import java.io.PrintWriter;
-
-/**
- * A view positioned under the notification shade.
- */
-public class LockIconView extends FrameLayout implements Dumpable {
- @IntDef({ICON_NONE, ICON_LOCK, ICON_FINGERPRINT, ICON_UNLOCK})
- public @interface IconType {}
-
- public static final int ICON_NONE = -1;
- public static final int ICON_LOCK = 0;
- public static final int ICON_FINGERPRINT = 1;
- public static final int ICON_UNLOCK = 2;
-
- private @IconType int mIconType;
- private boolean mAod;
-
- @NonNull private final RectF mSensorRect;
- @NonNull private Point mLockIconCenter = new Point(0, 0);
- private float mRadius;
- private int mLockIconPadding;
-
- private ImageView mLockIcon;
- private ImageView mBgView;
-
- private int mLockIconColor;
- private boolean mUseBackground = false;
- private float mDozeAmount = 0f;
-
- @SuppressLint("ClickableViewAccessibility")
- public LockIconView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mSensorRect = new RectF();
-
- addBgImageView(context, attrs);
- addLockIconImageView(context, attrs);
- }
-
- void setDozeAmount(float dozeAmount) {
- mDozeAmount = dozeAmount;
- updateColorAndBackgroundVisibility();
- }
-
- void updateColorAndBackgroundVisibility() {
- if (mUseBackground && mLockIcon.getDrawable() != null) {
- mLockIconColor = ColorUtils.blendARGB(
- Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary),
- Color.WHITE,
- mDozeAmount);
- int backgroundColor = Utils.getColorAttrDefaultColor(getContext(),
- com.android.internal.R.attr.colorSurface);
- mBgView.setImageTintList(ColorStateList.valueOf(backgroundColor));
- mBgView.setAlpha(1f - mDozeAmount);
- mBgView.setVisibility(View.VISIBLE);
- } else {
- mLockIconColor = ColorUtils.blendARGB(
- Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColorAccent),
- Color.WHITE,
- mDozeAmount);
- mBgView.setVisibility(View.GONE);
- }
-
- mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor));
- }
-
- /**
- * Whether or not to render the lock icon background. Mainly used for UDPFS.
- */
- public void setUseBackground(boolean useBackground) {
- mUseBackground = useBackground;
- updateColorAndBackgroundVisibility();
- }
-
- /**
- * Set the location of the lock icon.
- */
- public void setCenterLocation(@NonNull Point center, float radius, int drawablePadding) {
- mLockIconCenter = center;
- mRadius = radius;
- mLockIconPadding = drawablePadding;
-
- mLockIcon.setPadding(mLockIconPadding, mLockIconPadding, mLockIconPadding,
- mLockIconPadding);
-
- mSensorRect.set(mLockIconCenter.x - mRadius,
- mLockIconCenter.y - mRadius,
- mLockIconCenter.x + mRadius,
- mLockIconCenter.y + mRadius);
-
- final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
- if (lp != null) {
- lp.width = (int) (mSensorRect.right - mSensorRect.left);
- lp.height = (int) (mSensorRect.bottom - mSensorRect.top);
- lp.topMargin = (int) mSensorRect.top;
- lp.setMarginStart((int) mSensorRect.left);
- setLayoutParams(lp);
- }
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-
- float getLocationTop() {
- Rect r = new Rect();
- mLockIcon.getGlobalVisibleRect(r);
- return r.top;
- }
-
- float getLocationBottom() {
- Rect r = new Rect();
- mLockIcon.getGlobalVisibleRect(r);
- return r.bottom;
-
- }
-
- /**
- * Updates the icon its default state where no visual is shown.
- */
- public void clearIcon() {
- updateIcon(ICON_NONE, false);
- }
-
- /**
- * Transition the current icon to a new state
- * @param icon type (ie: lock icon, unlock icon, fingerprint icon)
- * @param aod whether to use the aod icon variant (some icons don't have aod variants and will
- * therefore show no icon)
- */
- public void updateIcon(@IconType int icon, boolean aod) {
- mIconType = icon;
- mAod = aod;
-
- mLockIcon.setImageState(getLockIconState(mIconType, mAod), true);
- }
-
- public ImageView getLockIcon() {
- return mLockIcon;
- }
-
- private void addLockIconImageView(Context context, AttributeSet attrs) {
- mLockIcon = new ImageView(context, attrs);
- mLockIcon.setId(R.id.lock_icon);
- mLockIcon.setScaleType(ImageView.ScaleType.CENTER_CROP);
- mLockIcon.setImageDrawable(context.getDrawable(R.drawable.super_lock_icon));
- addView(mLockIcon);
- LayoutParams lp = (LayoutParams) mLockIcon.getLayoutParams();
- lp.height = MATCH_PARENT;
- lp.width = MATCH_PARENT;
- lp.gravity = Gravity.CENTER;
- mLockIcon.setLayoutParams(lp);
- }
-
- private void addBgImageView(Context context, AttributeSet attrs) {
- mBgView = new ImageView(context, attrs);
- mBgView.setId(R.id.lock_icon_bg);
- mBgView.setImageDrawable(context.getDrawable(R.drawable.fingerprint_bg));
- mBgView.setVisibility(View.INVISIBLE);
- addView(mBgView);
- LayoutParams lp = (LayoutParams) mBgView.getLayoutParams();
- lp.height = MATCH_PARENT;
- lp.width = MATCH_PARENT;
- mBgView.setLayoutParams(lp);
- }
-
- private static int[] getLockIconState(@IconType int icon, boolean aod) {
- if (icon == ICON_NONE) {
- return new int[0];
- }
-
- int[] lockIconState = new int[2];
- switch (icon) {
- case ICON_LOCK:
- lockIconState[0] = android.R.attr.state_first;
- break;
- case ICON_FINGERPRINT:
- lockIconState[0] = android.R.attr.state_middle;
- break;
- case ICON_UNLOCK:
- lockIconState[0] = android.R.attr.state_last;
- break;
- }
-
- if (aod) {
- lockIconState[1] = android.R.attr.state_single;
- } else {
- lockIconState[1] = -android.R.attr.state_single;
- }
-
- return lockIconState;
- }
-
- private String typeToString(@IconType int type) {
- switch (type) {
- case ICON_NONE:
- return "none";
- case ICON_LOCK:
- return "lock";
- case ICON_FINGERPRINT:
- return "fingerprint";
- case ICON_UNLOCK:
- return "unlock";
- }
-
- return "invalid";
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("Lock Icon View Parameters:");
- pw.println(" Center in px (x, y)= ("
- + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
- pw.println(" Radius in pixels: " + mRadius);
- pw.println(" Drawable padding: " + mLockIconPadding);
- pw.println(" mIconType=" + typeToString(mIconType));
- pw.println(" mAod=" + mAod);
- pw.println("Lock Icon View actual measurements:");
- pw.println(" topLeft= (" + getX() + ", " + getY() + ")");
- pw.println(" width=" + getWidth() + " height=" + getHeight());
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
index 10d5a0cc3dd5..c5012b01dd3e 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
@@ -17,13 +17,19 @@
package com.android.keyguard
import android.view.MotionEvent
+import android.view.View
/** Controls the [LockIconView]. */
interface LockIconViewController {
- fun setLockIconView(lockIconView: LockIconView)
+ fun setLockIconView(lockIconView: View)
+
fun getTop(): Float
+
fun getBottom(): Float
+
fun dozeTimeTick()
+
fun setAlpha(alpha: Float)
+
fun willHandleTouchWhileDozing(event: MotionEvent): Boolean
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 158623fa80af..1978bb89b5b2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -42,6 +42,7 @@ import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.Space;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
@@ -52,6 +53,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HapClientProfile;
@@ -428,10 +430,16 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
} catch (Resources.NotFoundException e) {
Log.i(TAG, "No hearing devices related tool config resource");
}
- final int listSize = toolItemList.size();
- for (int i = 0; i < listSize; i++) {
+ for (int i = 0; i < toolItemList.size(); i++) {
View view = createHearingToolView(context, toolItemList.get(i));
mRelatedToolsContainer.addView(view);
+ if (i != toolItemList.size() - 1) {
+ final int spaceSize = context.getResources().getDimensionPixelSize(
+ R.dimen.hearing_devices_layout_margin);
+ Space space = new Space(context);
+ space.setLayoutParams(new LinearLayout.LayoutParams(spaceSize, 0));
+ mRelatedToolsContainer.addView(space);
+ }
}
}
@@ -492,6 +500,10 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
TextView text = view.requireViewById(R.id.tool_name);
view.setContentDescription(item.getToolName());
icon.setImageDrawable(item.getToolIcon());
+ if (item.isCustomIcon()) {
+ icon.getDrawable().mutate().setTint(Utils.getColorAttr(context,
+ com.android.internal.R.attr.materialColorOnPrimaryContainer).getDefaultColor());
+ }
text.setText(item.getToolName());
Intent intent = item.getToolIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -517,7 +529,8 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
return new ToolItem(
context.getString(R.string.quick_settings_hearing_devices_live_caption_title),
context.getDrawable(R.drawable.ic_volume_odi_captions),
- LIVE_CAPTION_INTENT);
+ LIVE_CAPTION_INTENT,
+ /* isCustomIcon= */ true);
}
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java
index 2006726e6847..7e4c1e5e7b6e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java
@@ -41,7 +41,7 @@ public class HearingDevicesToolItemParser {
private static final String SPLIT_DELIMITER = "/";
private static final String RES_TYPE = "drawable";
@VisibleForTesting
- static final int MAX_NUM = 3;
+ static final int MAX_NUM = 2;
/**
* Parses the string arrays to create a list of {@link ToolItem}.
@@ -82,7 +82,8 @@ public class HearingDevicesToolItemParser {
useCustomIcons ? iconList.get(i)
: activityInfoList.get(i).loadIcon(packageManager),
new Intent(Intent.ACTION_MAIN).setComponent(
- activityInfoList.get(i).getComponentName())
+ activityInfoList.get(i).getComponentName()),
+ useCustomIcons
));
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt
index 66bb2b5e2328..ef03f0cdef79 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt
@@ -23,4 +23,5 @@ data class ToolItem(
var toolName: String = "",
var toolIcon: Drawable,
var toolIntent: Intent,
+ var isCustomIcon: Boolean,
)
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index cd9efaf6e6bb..610e3f8a8c84 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -39,6 +39,10 @@ import com.android.systemui.qs.tiles.impl.fontscaling.domain.FontScalingTileMapp
import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileDataInteractor
import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.HearingDevicesTileMapper
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileDataInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.qs.tiles.impl.inversion.domain.ColorInversionTileMapper
import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor
import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor
@@ -159,6 +163,13 @@ interface QSAccessibilityModule {
impl: NightDisplayTileDataInteractor
): QSTileAvailabilityInteractor
+ @Binds
+ @IntoMap
+ @StringKey(HEARING_DEVICES_TILE_SPEC)
+ fun provideHearingDevicesAvailabilityInteractor(
+ impl: HearingDevicesTileDataInteractor
+ ): QSTileAvailabilityInteractor
+
companion object {
const val COLOR_CORRECTION_TILE_SPEC = "color_correction"
const val COLOR_INVERSION_TILE_SPEC = "inversion"
@@ -191,7 +202,7 @@ interface QSAccessibilityModule {
factory: QSTileViewModelFactory.Static<ColorCorrectionTileModel>,
mapper: ColorCorrectionTileMapper,
stateInteractor: ColorCorrectionTileDataInteractor,
- userActionInteractor: ColorCorrectionUserActionInteractor
+ userActionInteractor: ColorCorrectionUserActionInteractor,
): QSTileViewModel =
factory.create(
TileSpec.create(COLOR_CORRECTION_TILE_SPEC),
@@ -223,7 +234,7 @@ interface QSAccessibilityModule {
factory: QSTileViewModelFactory.Static<ColorInversionTileModel>,
mapper: ColorInversionTileMapper,
stateInteractor: ColorInversionTileDataInteractor,
- userActionInteractor: ColorInversionUserActionInteractor
+ userActionInteractor: ColorInversionUserActionInteractor,
): QSTileViewModel =
factory.create(
TileSpec.create(COLOR_INVERSION_TILE_SPEC),
@@ -255,7 +266,7 @@ interface QSAccessibilityModule {
factory: QSTileViewModelFactory.Static<FontScalingTileModel>,
mapper: FontScalingTileMapper,
stateInteractor: FontScalingTileDataInteractor,
- userActionInteractor: FontScalingTileUserActionInteractor
+ userActionInteractor: FontScalingTileUserActionInteractor,
): QSTileViewModel =
factory.create(
TileSpec.create(FONT_SCALING_TILE_SPEC),
@@ -279,21 +290,6 @@ interface QSAccessibilityModule {
category = TileCategory.DISPLAY,
)
- @Provides
- @IntoMap
- @StringKey(HEARING_DEVICES_TILE_SPEC)
- fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
- QSTileConfig(
- tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC),
- uiConfig =
- QSTileUIConfig.Resource(
- iconRes = R.drawable.qs_hearing_devices_icon,
- labelRes = R.string.quick_settings_hearing_devices_label,
- ),
- instanceId = uiEventLogger.getNewInstanceId(),
- category = TileCategory.ACCESSIBILITY,
- )
-
/**
* Inject Reduce Bright Colors Tile into tileViewModelMap in QSModule. The tile is hidden
* behind a flag.
@@ -305,7 +301,7 @@ interface QSAccessibilityModule {
factory: QSTileViewModelFactory.Static<ReduceBrightColorsTileModel>,
mapper: ReduceBrightColorsTileMapper,
stateInteractor: ReduceBrightColorsTileDataInteractor,
- userActionInteractor: ReduceBrightColorsTileUserActionInteractor
+ userActionInteractor: ReduceBrightColorsTileUserActionInteractor,
): QSTileViewModel =
if (Flags.qsNewTilesFuture())
factory.create(
@@ -339,7 +335,7 @@ interface QSAccessibilityModule {
factory: QSTileViewModelFactory.Static<OneHandedModeTileModel>,
mapper: OneHandedModeTileMapper,
stateInteractor: OneHandedModeTileDataInteractor,
- userActionInteractor: OneHandedModeTileUserActionInteractor
+ userActionInteractor: OneHandedModeTileUserActionInteractor,
): QSTileViewModel =
if (Flags.qsNewTilesFuture())
factory.create(
@@ -376,7 +372,7 @@ interface QSAccessibilityModule {
factory: QSTileViewModelFactory.Static<NightDisplayTileModel>,
mapper: NightDisplayTileMapper,
stateInteractor: NightDisplayTileDataInteractor,
- userActionInteractor: NightDisplayTileUserActionInteractor
+ userActionInteractor: NightDisplayTileUserActionInteractor,
): QSTileViewModel =
if (Flags.qsNewTilesFuture())
factory.create(
@@ -386,5 +382,43 @@ interface QSAccessibilityModule {
mapper,
)
else StubQSTileViewModel
+
+ @Provides
+ @IntoMap
+ @StringKey(HEARING_DEVICES_TILE_SPEC)
+ fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+ QSTileConfig(
+ tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC),
+ uiConfig =
+ QSTileUIConfig.Resource(
+ iconRes = R.drawable.qs_hearing_devices_icon,
+ labelRes = R.string.quick_settings_hearing_devices_label,
+ ),
+ instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.ACCESSIBILITY,
+ )
+
+ /**
+ * Inject HearingDevices Tile into tileViewModelMap in QSModule. The tile is hidden behind a
+ * flag.
+ */
+ @Provides
+ @IntoMap
+ @StringKey(HEARING_DEVICES_TILE_SPEC)
+ fun provideHearingDevicesTileViewModel(
+ factory: QSTileViewModelFactory.Static<HearingDevicesTileModel>,
+ mapper: HearingDevicesTileMapper,
+ stateInteractor: HearingDevicesTileDataInteractor,
+ userActionInteractor: HearingDevicesTileUserActionInteractor,
+ ): QSTileViewModel {
+ return if (Flags.hearingAidsQsTileDialog() && Flags.qsNewTilesFuture()) {
+ factory.create(
+ TileSpec.create(HEARING_DEVICES_TILE_SPEC),
+ userActionInteractor,
+ stateInteractor,
+ mapper,
+ )
+ } else StubQSTileViewModel
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index c95a94e5e388..f6cc72431db0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -37,11 +37,9 @@ import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.log.core.LogLevel
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.statusbar.CircleReveal
@@ -87,7 +85,7 @@ constructor(
private val lightRevealScrim: LightRevealScrim,
private val authRippleInteractor: AuthRippleInteractor,
private val facePropertyRepository: FacePropertyRepository,
- rippleView: AuthRippleView?
+ rippleView: AuthRippleView?,
) :
ViewController<AuthRippleView>(rippleView),
CoreStartable,
@@ -108,15 +106,13 @@ constructor(
}
init {
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- rippleView?.repeatWhenAttached {
- repeatOnLifecycle(androidx.lifecycle.Lifecycle.State.CREATED) {
- authRippleInteractor.showUnlockRipple.collect { biometricUnlockSource ->
- if (biometricUnlockSource == BiometricUnlockSource.FINGERPRINT_SENSOR) {
- showUnlockRippleInternal(BiometricSourceType.FINGERPRINT)
- } else {
- showUnlockRippleInternal(BiometricSourceType.FACE)
- }
+ rippleView?.repeatWhenAttached {
+ repeatOnLifecycle(androidx.lifecycle.Lifecycle.State.CREATED) {
+ authRippleInteractor.showUnlockRipple.collect { biometricUnlockSource ->
+ if (biometricUnlockSource == BiometricUnlockSource.FINGERPRINT_SENSOR) {
+ showUnlockRippleInternal(BiometricSourceType.FINGERPRINT)
+ } else {
+ showUnlockRippleInternal(BiometricSourceType.FACE)
}
}
}
@@ -134,29 +130,8 @@ constructor(
keyguardStateController.addCallback(this)
wakefulnessLifecycle.addObserver(this)
commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
- if (!DeviceEntryUdfpsRefactor.isEnabled) {
- biometricUnlockController.addListener(biometricModeListener)
- }
}
- private val biometricModeListener =
- object : BiometricUnlockController.BiometricUnlockEventsListener {
- override fun onBiometricUnlockedWithKeyguardDismissal(
- biometricSourceType: BiometricSourceType?
- ) {
- DeviceEntryUdfpsRefactor.assertInLegacyMode()
- if (biometricSourceType != null) {
- showUnlockRippleInternal(biometricSourceType)
- } else {
- logger.log(
- TAG,
- LogLevel.ERROR,
- "Unexpected scenario where biometricSourceType is null"
- )
- }
- }
- }
-
@VisibleForTesting
public override fun onViewDetached() {
udfpsController?.removeCallback(udfpsControllerCallback)
@@ -166,17 +141,10 @@ constructor(
keyguardStateController.removeCallback(this)
wakefulnessLifecycle.removeObserver(this)
commandRegistry.unregisterCommand("auth-ripple")
- biometricUnlockController.removeListener(biometricModeListener)
notificationShadeWindowController.setForcePluginOpen(false, this)
}
- @Deprecated("Update authRippleInteractor.showUnlockRipple instead of calling this.")
- fun showUnlockRipple(biometricSourceType: BiometricSourceType) {
- DeviceEntryUdfpsRefactor.assertInLegacyMode()
- showUnlockRippleInternal(biometricSourceType)
- }
-
private fun showUnlockRippleInternal(biometricSourceType: BiometricSourceType) {
val keyguardNotShowing = !keyguardStateController.isShowing
val unlockNotAllowed =
@@ -197,8 +165,8 @@ constructor(
0,
Math.max(
Math.max(it.x, displayMetrics.widthPixels - it.x),
- Math.max(it.y, displayMetrics.heightPixels - it.y)
- )
+ Math.max(it.y, displayMetrics.heightPixels - it.y),
+ ),
)
logger.showingUnlockRippleAt(it.x, it.y, "FP sensor radius: $udfpsRadius")
showUnlockedRipple()
@@ -213,8 +181,8 @@ constructor(
0,
Math.max(
Math.max(it.x, displayMetrics.widthPixels - it.x),
- Math.max(it.y, displayMetrics.heightPixels - it.y)
- )
+ Math.max(it.y, displayMetrics.heightPixels - it.y),
+ ),
)
logger.showingUnlockRippleAt(it.x, it.y, "Face unlock ripple")
showUnlockedRipple()
@@ -322,7 +290,7 @@ constructor(
override fun onBiometricAuthenticated(
userId: Int,
biometricSourceType: BiometricSourceType,
- isStrongBiometric: Boolean
+ isStrongBiometric: Boolean,
) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
mView.fadeDwellRipple()
@@ -337,7 +305,7 @@ constructor(
override fun onBiometricAcquired(
biometricSourceType: BiometricSourceType,
- acquireInfo: Int
+ acquireInfo: Int,
) {
if (
biometricSourceType == BiometricSourceType.FINGERPRINT &&
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt
deleted file mode 100644
index 242601d46fa4..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import android.content.Context
-import android.util.AttributeSet
-
-/**
- * Class that coordinates non-HBM animations during BiometricPrompt.
- *
- * Currently doesn't draw anything.
- *
- * Note that [AuthBiometricFingerprintViewController] also shows UDFPS animations. At some point we should
- * de-dupe this if necessary.
- */
-class UdfpsBpView(context: Context, attrs: AttributeSet?) : UdfpsAnimationView(context, attrs) {
-
- // Drawable isn't ever added to the view, so we don't currently show anything
- private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context)
-
- override fun getDrawable(): UdfpsDrawable = fingerprintDrawable
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
deleted file mode 100644
index e0455b58b919..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-
-/**
- * Class that coordinates non-HBM animations for biometric prompt.
- */
-class UdfpsBpViewController(
- view: UdfpsBpView,
- statusBarStateController: StatusBarStateController,
- shadeInteractor: ShadeInteractor,
- systemUIDialogManager: SystemUIDialogManager,
- dumpManager: DumpManager,
- udfpsOverlayInteractor: UdfpsOverlayInteractor,
-) : UdfpsAnimationViewController<UdfpsBpView>(
- view,
- statusBarStateController,
- shadeInteractor,
- systemUIDialogManager,
- dumpManager,
- udfpsOverlayInteractor,
-) {
- override val tag = "UdfpsBpViewController"
-
- override fun shouldPauseAuth(): Boolean {
- return false
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index a3904caa9dcc..2863e29c9a34 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -87,7 +87,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.ScreenLifecycle;
@@ -98,7 +97,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
@@ -162,7 +160,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
- @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final SystemClock mSystemClock;
@NonNull private final UnlockedScreenOffAnimationController
@@ -283,7 +280,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mKeyguardUpdateMonitor,
mDialogManager,
mDumpManager,
- mLockscreenShadeTransitionController,
mConfigurationController,
mKeyguardStateController,
mUnlockedScreenOffAnimationController,
@@ -291,10 +287,9 @@ public class UdfpsController implements DozeReceiver, Dumpable {
requestId,
reason,
callback,
- (view, event, fromUdfpsView) -> onTouch(
+ (view, event) -> onTouch(
requestId,
- event,
- fromUdfpsView
+ event
),
mActivityTransitionAnimator,
mPrimaryBouncerInteractor,
@@ -374,9 +369,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (mOverlay == null || mOverlay.isHiding()) {
return;
}
- if (!DeviceEntryUdfpsRefactor.isEnabled()) {
- ((UdfpsView) mOverlay.getTouchOverlay()).setDebugMessage(message);
- }
});
}
@@ -391,7 +383,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
*/
public void debugOnTouch(MotionEvent event) {
final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L;
- UdfpsController.this.onTouch(requestId, event, true);
+ UdfpsController.this.onTouch(requestId, event);
}
/**
@@ -449,22 +441,10 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (!mOverlayParams.equals(overlayParams)) {
mOverlayParams = overlayParams;
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) {
- mOverlay.updateOverlayParams(mOverlayParams);
- } else {
- redrawOverlay();
- }
+ if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) {
+ mOverlay.updateOverlayParams(mOverlayParams);
} else {
- final boolean wasShowingAlternateBouncer =
- mAlternateBouncerInteractor.isVisibleState();
- // When the bounds change it's always to re-create the overlay's window with new
- // LayoutParams. If the overlay needs to be shown, this will re-create and show the
- // overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden.
redrawOverlay();
- if (wasShowingAlternateBouncer) {
- mKeyguardViewManager.showBouncer(true);
- }
}
}
}
@@ -563,11 +543,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
}
- private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
- if (!fromUdfpsView) {
- Log.e(TAG, "ignoring the touch injected from outside of UdfpsView");
- return false;
- }
+ private boolean onTouch(long requestId, @NonNull MotionEvent event) {
if (mOverlay == null) {
Log.w(TAG, "ignoring onTouch with null overlay");
return false;
@@ -591,13 +567,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (!mIsAodInterruptActive) {
mOnFingerDown = false;
}
- } else if (!DeviceEntryUdfpsRefactor.isEnabled()) {
- if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f
- && !mAlternateBouncerInteractor.isVisibleState())
- || mPrimaryBouncerInteractor.isInTransit()) {
- Log.w(TAG, "ignoring touch due to qsDragProcess or primaryBouncerInteractor");
- return false;
- }
}
final TouchProcessorResult result = mTouchProcessor.processTouch(event, mActivePointerId,
@@ -661,22 +630,13 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mStatusBarStateController.isDozing());
break;
- case UNCHANGED:
- if (mActivePointerId == MotionEvent.INVALID_POINTER_ID
- && mAlternateBouncerInteractor.isVisibleState()) {
- // No pointer on sensor, forward to keyguard if alternateBouncer is visible
- mKeyguardViewManager.onTouch(event);
- }
-
default:
break;
}
logBiometricTouch(processedTouch.getEvent(), data);
// Always pilfer pointers that are within sensor area or when alternate bouncer is showing
- if (mActivePointerId != MotionEvent.INVALID_POINTER_ID
- || (mAlternateBouncerInteractor.isVisibleState()
- && !DeviceEntryUdfpsRefactor.isEnabled())) {
+ if (mActivePointerId != MotionEvent.INVALID_POINTER_ID) {
shouldPilfer = true;
}
@@ -692,14 +652,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
}
private boolean shouldTryToDismissKeyguard() {
- boolean onKeyguard = false;
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- onKeyguard = mKeyguardStateController.isShowing();
- } else {
- onKeyguard = mOverlay != null
- && mOverlay.getAnimationViewController()
- instanceof UdfpsKeyguardViewControllerLegacy;
- }
+ boolean onKeyguard = mKeyguardStateController.isShowing();
return onKeyguard
&& mKeyguardStateController.canDismissLockScreen()
&& !mAttemptedToDismissKeyguard;
@@ -719,7 +672,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull FalsingManager falsingManager,
@NonNull PowerManager powerManager,
@NonNull AccessibilityManager accessibilityManager,
- @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController,
@NonNull ScreenLifecycle screenLifecycle,
@NonNull VibratorHelper vibrator,
@NonNull UdfpsHapticsSimulator udfpsHapticsSimulator,
@@ -769,7 +721,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mFalsingManager = falsingManager;
mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager;
- mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
screenLifecycle.addObserver(mScreenObserver);
mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
mConfigurationController = configurationController;
@@ -849,13 +800,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@Override
public void dozeTimeTick() {
- if (mOverlay != null && mOverlay.getTouchOverlay() instanceof UdfpsView) {
- DeviceEntryUdfpsRefactor.assertInLegacyMode();
- final View view = mOverlay.getTouchOverlay();
- if (view != null) {
- ((UdfpsView) view).dozeTimeTick();
- }
- }
+
}
private void redrawOverlay() {
@@ -915,17 +860,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (!isOptical()) {
return;
}
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- if (mUdfpsDisplayMode != null) {
- mUdfpsDisplayMode.disable(null);
- }
- } else {
- if (view != null) {
- UdfpsView udfpsView = (UdfpsView) view;
- if (udfpsView.isDisplayConfigured()) {
- udfpsView.unconfigureDisplay();
- }
- }
+ if (mUdfpsDisplayMode != null) {
+ mUdfpsDisplayMode.disable(null);
}
}
@@ -1118,11 +1054,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
if (mIgnoreRefreshRate) {
dispatchOnUiReady(requestId);
} else {
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
mUdfpsDisplayMode.enable(() -> dispatchOnUiReady(requestId));
- } else {
- ((UdfpsView) view).configureDisplay(() -> dispatchOnUiReady(requestId));
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 1bac0bc26a94..a1efc196dbee 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -23,8 +23,6 @@ import android.graphics.PixelFormat
import android.graphics.Rect
import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR
import android.hardware.biometrics.BiometricRequestConstants.RequestReason
@@ -42,7 +40,6 @@ import android.view.View
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
-import androidx.annotation.LayoutRes
import androidx.annotation.VisibleForTesting
import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.keyguard.KeyguardUpdateMonitor
@@ -56,7 +53,6 @@ import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlay
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -64,7 +60,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
@@ -102,7 +97,6 @@ constructor(
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val dialogManager: SystemUIDialogManager,
private val dumpManager: DumpManager,
- private val transitionController: LockscreenShadeTransitionController,
private val configurationController: ConfigurationController,
private val keyguardStateController: KeyguardStateController,
private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
@@ -110,7 +104,7 @@ constructor(
val requestId: Long,
@RequestReason val requestReason: Int,
private val controllerCallback: IUdfpsOverlayControllerCallback,
- private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
+ private val onTouch: (View, MotionEvent) -> Boolean,
private val activityTransitionAnimator: ActivityTransitionAnimator,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
@@ -133,23 +127,15 @@ constructor(
.map {} // map to Unit
private var listenForCurrentKeyguardState: Job? = null
private var addViewRunnable: Runnable? = null
- private var overlayViewLegacy: UdfpsView? = null
- private set
-
private var overlayTouchView: UdfpsTouchOverlay? = null
/**
- * Get the current UDFPS overlay touch view which is a different View depending on whether the
- * DeviceEntryUdfpsRefactor flag is enabled or not.
+ * Get the current UDFPS overlay touch view
*
* @return The view, when [isShowing], else null
*/
fun getTouchOverlay(): View? {
- return if (DeviceEntryUdfpsRefactor.isEnabled) {
- overlayTouchView
- } else {
- overlayViewLegacy
- }
+ return overlayTouchView
}
private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
@@ -161,7 +147,7 @@ constructor(
WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
0 /* flags set in computeLayoutParams() */,
- PixelFormat.TRANSLUCENT
+ PixelFormat.TRANSLUCENT,
)
.apply {
title = TAG
@@ -188,10 +174,6 @@ constructor(
val isHiding: Boolean
get() = getTouchOverlay() == null
- /** The animation controller if the overlay [isShowing]. */
- val animationViewController: UdfpsAnimationViewController<*>?
- get() = overlayViewLegacy?.animationViewController
-
private var touchExplorationEnabled = false
private fun shouldRemoveEnrollmentUi(): Boolean {
@@ -199,7 +181,7 @@ constructor(
return Settings.Global.getInt(
context.contentResolver,
SETTING_REMOVE_ENROLLMENT_UI,
- 0 /* def */
+ 0, /* def */
) != 0
}
return false
@@ -212,63 +194,43 @@ constructor(
overlayParams = params
sensorBounds = Rect(params.sensorBounds)
try {
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- overlayTouchView =
- (inflater.inflate(R.layout.udfps_touch_overlay, null, false)
- as UdfpsTouchOverlay)
- .apply {
- // This view overlaps the sensor area
- // prevent it from being selectable during a11y
- if (requestReason.isImportantForAccessibility()) {
- importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
- }
-
- addViewNowOrLater(this, null)
- when (requestReason) {
- REASON_AUTH_KEYGUARD ->
- UdfpsTouchOverlayBinder.bind(
- view = this,
- viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(),
- udfpsOverlayInteractor = udfpsOverlayInteractor,
- )
- else ->
- UdfpsTouchOverlayBinder.bind(
- view = this,
- viewModel = defaultUdfpsTouchOverlayViewModel.get(),
- udfpsOverlayInteractor = udfpsOverlayInteractor,
- )
- }
- }
- } else {
- overlayViewLegacy =
- (inflater.inflate(R.layout.udfps_view, null, false) as UdfpsView).apply {
- overlayParams = params
- setUdfpsDisplayModeProvider(udfpsDisplayModeProvider)
- val animation = inflateUdfpsAnimation(this, controller)
- if (animation != null) {
- animation.init()
- animationViewController = animation
- }
+ overlayTouchView =
+ (inflater.inflate(R.layout.udfps_touch_overlay, null, false)
+ as UdfpsTouchOverlay)
+ .apply {
// This view overlaps the sensor area
// prevent it from being selectable during a11y
if (requestReason.isImportantForAccessibility()) {
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
}
- addViewNowOrLater(this, animation)
- sensorRect = sensorBounds
+ addViewNowOrLater(this, null)
+ when (requestReason) {
+ REASON_AUTH_KEYGUARD ->
+ UdfpsTouchOverlayBinder.bind(
+ view = this,
+ viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(),
+ udfpsOverlayInteractor = udfpsOverlayInteractor,
+ )
+ else ->
+ UdfpsTouchOverlayBinder.bind(
+ view = this,
+ viewModel = defaultUdfpsTouchOverlayViewModel.get(),
+ udfpsOverlayInteractor = udfpsOverlayInteractor,
+ )
+ }
}
- }
+
getTouchOverlay()?.apply {
touchExplorationEnabled = accessibilityManager.isTouchExplorationEnabled
overlayTouchListener = TouchExplorationStateChangeListener {
if (accessibilityManager.isTouchExplorationEnabled) {
- setOnHoverListener { v, event -> onTouch(v, event, true) }
+ setOnHoverListener { v, event -> onTouch(v, event) }
setOnTouchListener(null)
touchExplorationEnabled = true
} else {
setOnHoverListener(null)
- setOnTouchListener { v, event -> onTouch(v, event, true) }
+ setOnTouchListener { v, event -> onTouch(v, event) }
touchExplorationEnabled = false
}
}
@@ -312,7 +274,6 @@ constructor(
}
fun updateOverlayParams(updatedOverlayParams: UdfpsOverlayParams) {
- DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
overlayParams = updatedOverlayParams
sensorBounds = updatedOverlayParams.sensorBounds
getTouchOverlay()?.let {
@@ -326,108 +287,11 @@ constructor(
}
}
- fun inflateUdfpsAnimation(
- view: UdfpsView,
- controller: UdfpsController
- ): UdfpsAnimationViewController<*>? {
- DeviceEntryUdfpsRefactor.assertInLegacyMode()
-
- val isEnrollment =
- when (requestReason) {
- REASON_ENROLL_FIND_SENSOR,
- REASON_ENROLL_ENROLLING -> true
- else -> false
- }
-
- val filteredRequestReason =
- if (isEnrollment && shouldRemoveEnrollmentUi()) {
- REASON_AUTH_OTHER
- } else {
- requestReason
- }
-
- return when (filteredRequestReason) {
- REASON_ENROLL_FIND_SENSOR,
- REASON_ENROLL_ENROLLING -> {
- // Enroll udfps UI is handled by settings, so use empty view here
- UdfpsFpmEmptyViewController(
- view.addUdfpsView(R.layout.udfps_fpm_empty_view) {
- updateAccessibilityViewLocation(sensorBounds)
- },
- statusBarStateController,
- shadeInteractor,
- dialogManager,
- dumpManager,
- udfpsOverlayInteractor,
- )
- }
- REASON_AUTH_KEYGUARD -> {
- UdfpsKeyguardViewControllerLegacy(
- view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) {
- updateSensorLocation(sensorBounds)
- },
- statusBarStateController,
- statusBarKeyguardViewManager,
- keyguardUpdateMonitor,
- dumpManager,
- transitionController,
- configurationController,
- keyguardStateController,
- unlockedScreenOffAnimationController,
- dialogManager,
- controller,
- activityTransitionAnimator,
- primaryBouncerInteractor,
- alternateBouncerInteractor,
- udfpsKeyguardAccessibilityDelegate,
- selectedUserInteractor,
- transitionInteractor,
- shadeInteractor,
- udfpsOverlayInteractor,
- )
- }
- REASON_AUTH_BP -> {
- // note: empty controller, currently shows no visual affordance
- UdfpsBpViewController(
- view.addUdfpsView(R.layout.udfps_bp_view),
- statusBarStateController,
- shadeInteractor,
- dialogManager,
- dumpManager,
- udfpsOverlayInteractor,
- )
- }
- REASON_AUTH_OTHER,
- REASON_AUTH_SETTINGS -> {
- UdfpsFpmEmptyViewController(
- view.addUdfpsView(R.layout.udfps_fpm_empty_view),
- statusBarStateController,
- shadeInteractor,
- dialogManager,
- dumpManager,
- udfpsOverlayInteractor,
- )
- }
- else -> {
- Log.e(TAG, "Animation for reason $requestReason not supported yet")
- null
- }
- }
- }
-
/** Hide the overlay or return false and do nothing if it is already hidden. */
fun hide(): Boolean {
val wasShowing = isShowing
- overlayViewLegacy?.apply {
- if (isDisplayConfigured) {
- unconfigureDisplay()
- }
- animationViewController = null
- }
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- udfpsDisplayModeProvider.disable(null)
- }
+ udfpsDisplayModeProvider.disable(null)
getTouchOverlay()?.apply {
if (this.parent != null) {
windowManager.removeView(this)
@@ -440,7 +304,6 @@ constructor(
}
}
- overlayViewLegacy = null
overlayTouchView = null
overlayTouchListener = null
listenForCurrentKeyguardState?.cancel()
@@ -490,7 +353,7 @@ constructor(
Surface.rotationToString(rot) +
" animation=$animation" +
" isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
- " isOccluded=${keyguardStateController.isOccluded}"
+ " isOccluded=${keyguardStateController.isOccluded}",
)
} else {
Log.v(TAG, "Rotate UDFPS bounds " + Surface.rotationToString(rot))
@@ -498,14 +361,14 @@ constructor(
rotatedBounds,
overlayParams.naturalDisplayWidth,
overlayParams.naturalDisplayHeight,
- rot
+ rot,
)
RotationUtils.rotateBounds(
sensorBounds,
overlayParams.naturalDisplayWidth,
overlayParams.naturalDisplayHeight,
- rot
+ rot,
)
}
}
@@ -519,14 +382,7 @@ constructor(
}
private fun shouldRotate(animation: UdfpsAnimationViewController<*>?): Boolean {
- val keyguardNotShowing =
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- !keyguardStateController.isShowing
- } else {
- animation !is UdfpsKeyguardViewControllerLegacy
- }
-
- if (keyguardNotShowing) {
+ if (!keyguardStateController.isShowing) {
// always rotate view if we're not on the keyguard
return true
}
@@ -534,16 +390,6 @@ constructor(
// on the keyguard, make sure we don't rotate if we're going to sleep or not occluded
return !(keyguardUpdateMonitor.isGoingToSleep || !keyguardStateController.isOccluded)
}
-
- private inline fun <reified T : View> UdfpsView.addUdfpsView(
- @LayoutRes id: Int,
- init: T.() -> Unit = {}
- ): T {
- val subView = inflater.inflate(id, null) as T
- addView(subView)
- subView.init()
- return subView
- }
}
@RequestReason
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt
deleted file mode 100644
index 0838855f4756..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import android.content.Context
-import android.graphics.Rect
-import android.util.AttributeSet
-import android.view.View
-import android.view.ViewGroup
-import com.android.systemui.res.R
-
-/**
- * View corresponding with udfps_fpm_empty_view.xml
- *
- * Currently doesn't draw anything.
- */
-class UdfpsFpmEmptyView(
- context: Context,
- attrs: AttributeSet?
-) : UdfpsAnimationView(context, attrs) {
-
- // Drawable isn't ever added to the view, so we don't currently show anything
- private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context)
-
- override fun getDrawable(): UdfpsDrawable = fingerprintDrawable
-
- fun updateAccessibilityViewLocation(sensorBounds: Rect) {
- val fingerprintAccessibilityView: View =
- requireViewById(R.id.udfps_enroll_accessibility_view)
- val params: ViewGroup.LayoutParams = fingerprintAccessibilityView.layoutParams
- params.width = sensorBounds.width()
- params.height = sensorBounds.height()
- fingerprintAccessibilityView.layoutParams = params
- fingerprintAccessibilityView.requestLayout()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
deleted file mode 100644
index cfbbc26800d2..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-
-/**
- * Class that coordinates non-HBM animations for non keyguard, or biometric prompt states.
- *
- * Currently doesn't draw anything.
- */
-class UdfpsFpmEmptyViewController(
- view: UdfpsFpmEmptyView,
- statusBarStateController: StatusBarStateController,
- shadeInteractor: ShadeInteractor,
- systemUIDialogManager: SystemUIDialogManager,
- dumpManager: DumpManager,
- udfpsOverlayInteractor: UdfpsOverlayInteractor,
-) : UdfpsAnimationViewController<UdfpsFpmEmptyView>(
- view,
- statusBarStateController,
- shadeInteractor,
- systemUIDialogManager,
- dumpManager,
- udfpsOverlayInteractor,
-) {
- override val tag = "UdfpsFpmOtherViewController"
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
deleted file mode 100644
index c3d9240c40a1..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.content.res.Configuration
-import android.util.MathUtils
-import android.view.View
-import androidx.annotation.VisibleForTesting
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.animation.Interpolators
-import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.animation.ActivityTransitionAnimator
-import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF
-import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.Edge
-import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
-import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
-import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
-import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
-import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.res.R
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.LockscreenShadeTransitionController
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.KeyguardViewManagerCallback
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.OccludingAppBiometricUI
-import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import java.io.PrintWriter
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-
-/** Class that coordinates non-HBM animations during keyguard authentication. */
-@ExperimentalCoroutinesApi
-open class UdfpsKeyguardViewControllerLegacy(
- private val view: UdfpsKeyguardViewLegacy,
- statusBarStateController: StatusBarStateController,
- private val keyguardViewManager: StatusBarKeyguardViewManager,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- dumpManager: DumpManager,
- private val lockScreenShadeTransitionController: LockscreenShadeTransitionController,
- private val configurationController: ConfigurationController,
- private val keyguardStateController: KeyguardStateController,
- private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
- systemUIDialogManager: SystemUIDialogManager,
- private val udfpsController: UdfpsController,
- private val activityTransitionAnimator: ActivityTransitionAnimator,
- private val primaryBouncerInteractor: PrimaryBouncerInteractor,
- private val alternateBouncerInteractor: AlternateBouncerInteractor,
- private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
- private val selectedUserInteractor: SelectedUserInteractor,
- private val transitionInteractor: KeyguardTransitionInteractor,
- shadeInteractor: ShadeInteractor,
- udfpsOverlayInteractor: UdfpsOverlayInteractor,
-) :
- UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>(
- view,
- statusBarStateController,
- shadeInteractor,
- systemUIDialogManager,
- dumpManager,
- udfpsOverlayInteractor,
- ) {
- private val uniqueIdentifier = this.toString()
- private var showingUdfpsBouncer = false
- private var udfpsRequested = false
- private var qsExpansion = 0f
- private var faceDetectRunning = false
- private var statusBarState = 0
- private var transitionToFullShadeProgress = 0f
- private var lastDozeAmount = 0f
- private var panelExpansionFraction = 0f
- private var launchTransitionFadingAway = false
- private var isLaunchingActivity = false
- private var activityLaunchProgress = 0f
- private var inputBouncerExpansion = 0f
-
- private val stateListener: StatusBarStateController.StateListener =
- object : StatusBarStateController.StateListener {
- override fun onStateChanged(statusBarState: Int) {
- this@UdfpsKeyguardViewControllerLegacy.statusBarState = statusBarState
- updateAlpha()
- updatePauseAuth()
- }
- }
-
- private val configurationListener: ConfigurationController.ConfigurationListener =
- object : ConfigurationController.ConfigurationListener {
- override fun onUiModeChanged() {
- view.updateColor()
- }
-
- override fun onThemeChanged() {
- view.updateColor()
- }
-
- override fun onConfigChanged(newConfig: Configuration) {
- updateScaleFactor()
- view.updatePadding()
- view.updateColor()
- }
- }
-
- private val keyguardStateControllerCallback: KeyguardStateController.Callback =
- object : KeyguardStateController.Callback {
- override fun onUnlockedChanged() {
- updatePauseAuth()
- }
-
- override fun onLaunchTransitionFadingAwayChanged() {
- launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway
- updatePauseAuth()
- }
- }
-
- private val mActivityTransitionAnimatorListener: ActivityTransitionAnimator.Listener =
- object : ActivityTransitionAnimator.Listener {
- override fun onTransitionAnimationStart() {
- isLaunchingActivity = true
- activityLaunchProgress = 0f
- updateAlpha()
- }
-
- override fun onTransitionAnimationEnd() {
- isLaunchingActivity = false
- updateAlpha()
- }
-
- override fun onTransitionAnimationProgress(linearProgress: Float) {
- activityLaunchProgress = linearProgress
- updateAlpha()
- }
- }
-
- private val statusBarKeyguardViewManagerCallback: KeyguardViewManagerCallback =
- object : KeyguardViewManagerCallback {
- override fun onQSExpansionChanged(qsExpansion: Float) {
- this@UdfpsKeyguardViewControllerLegacy.qsExpansion = qsExpansion
- updateAlpha()
- updatePauseAuth()
- }
- }
-
- private val occludingAppBiometricUI: OccludingAppBiometricUI =
- object : OccludingAppBiometricUI {
- override fun requestUdfps(request: Boolean, color: Int) {
- udfpsRequested = request
- view.requestUdfps(request, color)
- updateAlpha()
- updatePauseAuth()
- }
-
- override fun dump(pw: PrintWriter) {
- pw.println(tag)
- }
- }
-
- override val tag: String
- get() = TAG
-
- override fun onInit() {
- super.onInit()
- keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI)
- }
-
- init {
- com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor.assertInLegacyMode()
- view.repeatWhenAttached {
- // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion
- // can make the view not visible; and we still want to listen for events
- // that may make the view visible again.
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- listenForBouncerExpansion(this)
- listenForAlternateBouncerVisibility(this)
- listenForOccludedToAodTransition(this)
- listenForGoneToAodTransition(this)
- listenForLockscreenAodTransitions(this)
- listenForAodToOccludedTransitions(this)
- listenForAlternateBouncerToAodTransitions(this)
- listenForDreamingToAodTransitions(this)
- listenForPrimaryBouncerToAodTransitions(this)
- }
- }
- }
-
- @VisibleForTesting
- suspend fun listenForPrimaryBouncerToAodTransitions(scope: CoroutineScope): Job {
- return scope.launch {
- transitionInteractor
- .transition(
- edge = Edge.create(Scenes.Bouncer, AOD),
- edgeWithoutSceneContainer = Edge.create(PRIMARY_BOUNCER, AOD)
- )
- .collect { transitionStep ->
- view.onDozeAmountChanged(
- transitionStep.value,
- transitionStep.value,
- ANIMATE_APPEAR_ON_SCREEN_OFF,
- )
- }
- }
- }
-
- @VisibleForTesting
- suspend fun listenForDreamingToAodTransitions(scope: CoroutineScope): Job {
- return scope.launch {
- transitionInteractor.transition(Edge.create(DREAMING, AOD)).collect { transitionStep ->
- view.onDozeAmountChanged(
- transitionStep.value,
- transitionStep.value,
- ANIMATE_APPEAR_ON_SCREEN_OFF,
- )
- }
- }
- }
-
- @VisibleForTesting
- suspend fun listenForAlternateBouncerToAodTransitions(scope: CoroutineScope): Job {
- return scope.launch {
- transitionInteractor.transition(Edge.create(ALTERNATE_BOUNCER, AOD)).collect {
- transitionStep ->
- view.onDozeAmountChanged(
- transitionStep.value,
- transitionStep.value,
- UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
- )
- }
- }
- }
-
- @VisibleForTesting
- suspend fun listenForAodToOccludedTransitions(scope: CoroutineScope): Job {
- return scope.launch {
- transitionInteractor.transition(Edge.create(AOD, OCCLUDED)).collect { transitionStep ->
- view.onDozeAmountChanged(
- 1f - transitionStep.value,
- 1f - transitionStep.value,
- UdfpsKeyguardViewLegacy.ANIMATION_NONE,
- )
- }
- }
- }
-
- @VisibleForTesting
- suspend fun listenForOccludedToAodTransition(scope: CoroutineScope): Job {
- return scope.launch {
- transitionInteractor.transition(Edge.create(OCCLUDED, AOD)).collect { transitionStep ->
- view.onDozeAmountChanged(
- transitionStep.value,
- transitionStep.value,
- ANIMATE_APPEAR_ON_SCREEN_OFF,
- )
- }
- }
- }
-
- @VisibleForTesting
- suspend fun listenForGoneToAodTransition(scope: CoroutineScope): Job {
- return scope.launch {
- transitionInteractor
- .transition(
- edge = Edge.create(Scenes.Gone, AOD),
- edgeWithoutSceneContainer = Edge.create(GONE, AOD)
- )
- .collect { transitionStep ->
- view.onDozeAmountChanged(
- transitionStep.value,
- transitionStep.value,
- ANIMATE_APPEAR_ON_SCREEN_OFF,
- )
- }
- }
- }
-
- @VisibleForTesting
- suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job {
- return scope.launch {
- transitionInteractor.transitionValue(AOD).collect {
- view.onDozeAmountChanged(
- it,
- it,
- UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
- )
- }
- }
- }
-
- @VisibleForTesting
- suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
- return scope.launch {
- primaryBouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float ->
- inputBouncerExpansion = bouncerExpansion
-
- panelExpansionFraction =
- if (keyguardViewManager.isPrimaryBouncerInTransit) {
- aboutToShowBouncerProgress(1f - bouncerExpansion)
- } else {
- 1f - bouncerExpansion
- }
- updateAlpha()
- updatePauseAuth()
- }
- }
- }
-
- @VisibleForTesting
- suspend fun listenForAlternateBouncerVisibility(scope: CoroutineScope): Job {
- return scope.launch {
- alternateBouncerInteractor.isVisible.collect { isVisible: Boolean ->
- showUdfpsBouncer(isVisible)
- }
- }
- }
-
- public override fun onViewAttached() {
- super.onViewAttached()
- alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, uniqueIdentifier)
- val dozeAmount = statusBarStateController.dozeAmount
- lastDozeAmount = dozeAmount
- stateListener.onDozeAmountChanged(dozeAmount, dozeAmount)
- statusBarStateController.addCallback(stateListener)
- udfpsRequested = false
- launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway
- keyguardStateController.addCallback(keyguardStateControllerCallback)
- statusBarState = statusBarStateController.state
- qsExpansion = keyguardViewManager.qsExpansion
- keyguardViewManager.addCallback(statusBarKeyguardViewManagerCallback)
- configurationController.addCallback(configurationListener)
- updateScaleFactor()
- view.updatePadding()
- updateAlpha()
- updatePauseAuth()
- keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI)
- lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = this
- activityTransitionAnimator.addListener(mActivityTransitionAnimatorListener)
- view.startIconAsyncInflate {
- val animationViewInternal: View =
- view.requireViewById(R.id.udfps_animation_view_internal)
- animationViewInternal.accessibilityDelegate = udfpsKeyguardAccessibilityDelegate
- }
- }
-
- public override fun onViewDetached() {
- super.onViewDetached()
- alternateBouncerInteractor.setAlternateBouncerUIAvailable(false, uniqueIdentifier)
- faceDetectRunning = false
- keyguardStateController.removeCallback(keyguardStateControllerCallback)
- statusBarStateController.removeCallback(stateListener)
- keyguardViewManager.removeOccludingAppBiometricUI(occludingAppBiometricUI)
- configurationController.removeCallback(configurationListener)
- if (lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy === this) {
- lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = null
- }
- activityTransitionAnimator.removeListener(mActivityTransitionAnimatorListener)
- keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback)
- }
-
- override fun dump(pw: PrintWriter, args: Array<String>) {
- super.dump(pw, args)
- pw.println("showingUdfpsAltBouncer=$showingUdfpsBouncer")
- pw.println(
- "altBouncerInteractor#isAlternateBouncerVisible=" +
- "${alternateBouncerInteractor.isVisibleState()}"
- )
- pw.println(
- "altBouncerInteractor#canShowAlternateBouncerForFingerprint=" +
- "${alternateBouncerInteractor.canShowAlternateBouncerForFingerprint()}"
- )
- pw.println("faceDetectRunning=$faceDetectRunning")
- pw.println("statusBarState=" + StatusBarState.toString(statusBarState))
- pw.println("transitionToFullShadeProgress=$transitionToFullShadeProgress")
- pw.println("qsExpansion=$qsExpansion")
- pw.println("panelExpansionFraction=$panelExpansionFraction")
- pw.println("unpausedAlpha=" + view.unpausedAlpha)
- pw.println("udfpsRequestedByApp=$udfpsRequested")
- pw.println("launchTransitionFadingAway=$launchTransitionFadingAway")
- pw.println("lastDozeAmount=$lastDozeAmount")
- pw.println("inputBouncerExpansion=$inputBouncerExpansion")
- view.dump(pw)
- }
-
- /**
- * Overrides non-bouncer show logic in shouldPauseAuth to still show icon.
- *
- * @return whether the udfpsBouncer has been newly shown or hidden
- */
- private fun showUdfpsBouncer(show: Boolean): Boolean {
- if (showingUdfpsBouncer == show) {
- return false
- }
- val udfpsAffordanceWasNotShowing = shouldPauseAuth()
- showingUdfpsBouncer = show
- if (showingUdfpsBouncer) {
- if (udfpsAffordanceWasNotShowing) {
- view.animateInUdfpsBouncer(null)
- }
- view.announceForAccessibility(
- view.context.getString(R.string.accessibility_fingerprint_bouncer)
- )
- }
- updateAlpha()
- updatePauseAuth()
- return true
- }
-
- /**
- * Returns true if the fingerprint manager is running but we want to temporarily pause
- * authentication. On the keyguard, we may want to show udfps when the shade is expanded, so
- * this can be overridden with the showBouncer method.
- */
- override fun shouldPauseAuth(): Boolean {
- if (showingUdfpsBouncer) {
- return false
- }
- if (
- udfpsRequested &&
- !notificationShadeVisible &&
- !isInputBouncerFullyVisible() &&
- keyguardStateController.isShowing
- ) {
- return false
- }
- if (launchTransitionFadingAway) {
- return true
- }
-
- // Only pause auth if we're not on the keyguard AND we're not transitioning to doze.
- // For the UnlockedScreenOffAnimation, the statusBarState is
- // delayed. However, we still animate in the UDFPS affordance with the
- // unlockedScreenOffDozeAnimator.
- if (
- statusBarState != StatusBarState.KEYGUARD &&
- !unlockedScreenOffAnimationController.isAnimationPlaying()
- ) {
- return true
- }
- if (isBouncerExpansionGreaterThan(.5f)) {
- return true
- }
- if (
- keyguardUpdateMonitor.getUserUnlockedWithBiometric(
- selectedUserInteractor.getSelectedUserId()
- )
- ) {
- // If the device was unlocked by a biometric, immediately hide the UDFPS icon to avoid
- // overlap with the LockIconView. Shortly afterwards, UDFPS will stop running.
- return true
- }
- return view.unpausedAlpha < 255 * .1
- }
-
- fun isBouncerExpansionGreaterThan(bouncerExpansionThreshold: Float): Boolean {
- return inputBouncerExpansion >= bouncerExpansionThreshold
- }
-
- fun isInputBouncerFullyVisible(): Boolean {
- return inputBouncerExpansion == 1f
- }
-
- override fun listenForTouchesOutsideView(): Boolean {
- return true
- }
-
- /**
- * Set the progress we're currently transitioning to the full shade. 0.0f means we're not
- * transitioning yet, while 1.0f means we've fully dragged down. For example, start swiping down
- * to expand the notification shade from the empty space in the middle of the lock screen.
- */
- fun setTransitionToFullShadeProgress(progress: Float) {
- transitionToFullShadeProgress = progress
- updateAlpha()
- }
-
- /**
- * Update alpha for the UDFPS lock screen affordance. The AoD UDFPS visual affordance's alpha is
- * based on the doze amount.
- */
- override fun updateAlpha() {
- // Fade icon on transitions to showing the status bar or bouncer, but if mUdfpsRequested,
- // then the keyguard is occluded by some application - so instead use the input bouncer
- // hidden amount to determine the fade.
- val expansion = if (udfpsRequested) getInputBouncerHiddenAmt() else panelExpansionFraction
- var alpha: Int =
- if (showingUdfpsBouncer) 255
- else MathUtils.constrain(MathUtils.map(.5f, .9f, 0f, 255f, expansion), 0f, 255f).toInt()
- if (!showingUdfpsBouncer) {
- // swipe from top of the lockscreen to expand full QS:
- alpha =
- (alpha * (1.0f - Interpolators.EMPHASIZED_DECELERATE.getInterpolation(qsExpansion)))
- .toInt()
-
- // swipe from the middle (empty space) of lockscreen to expand the notification shade:
- alpha = (alpha * (1.0f - transitionToFullShadeProgress)).toInt()
-
- // Fade out the icon if we are animating an activity launch over the lockscreen and the
- // activity didn't request the UDFPS.
- if (isLaunchingActivity && !udfpsRequested) {
- val udfpsActivityLaunchAlphaMultiplier =
- 1f -
- (activityLaunchProgress *
- (ActivityTransitionAnimator.TIMINGS.totalDuration / 83))
- .coerceIn(0f, 1f)
- alpha = (alpha * udfpsActivityLaunchAlphaMultiplier).toInt()
- }
-
- // Fade out alpha when a dialog is shown
- // Fade in alpha when a dialog is hidden
- alpha = (alpha * view.dialogSuggestedAlpha).toInt()
- }
- view.unpausedAlpha = alpha
- }
-
- private fun getInputBouncerHiddenAmt(): Float {
- return 1f - inputBouncerExpansion
- }
-
- /** Update the scale factor based on the device's resolution. */
- private fun updateScaleFactor() {
- udfpsController.mOverlayParams?.scaleFactor?.let { view.setScaleFactor(it) }
- }
-
- companion object {
- const val TAG = "UdfpsKeyguardViewController"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
deleted file mode 100644
index 6d4eea852d26..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInProgressOffset;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.util.AttributeSet;
-import android.util.MathUtils;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
-
-import com.android.app.animation.Interpolators;
-import com.android.settingslib.Utils;
-import com.android.systemui.res.R;
-
-import com.airbnb.lottie.LottieAnimationView;
-import com.airbnb.lottie.LottieProperty;
-import com.airbnb.lottie.model.KeyPath;
-
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * View corresponding with udfps_keyguard_view_legacy.xml
- */
-public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView {
- private UdfpsDrawable mFingerprintDrawable; // placeholder
- private LottieAnimationView mAodFp;
- private LottieAnimationView mLockScreenFp;
-
- // used when highlighting fp icon:
- private int mTextColorPrimary;
- private ImageView mBgProtection;
- boolean mUdfpsRequested;
-
- private AnimatorSet mBackgroundInAnimator = new AnimatorSet();
- private int mAlpha; // 0-255
- private float mScaleFactor = 1;
- private Rect mSensorBounds = new Rect();
-
- // AOD anti-burn-in offsets
- private final int mMaxBurnInOffsetX;
- private final int mMaxBurnInOffsetY;
- private float mInterpolatedDarkAmount;
- private int mAnimationType = ANIMATION_NONE;
- private boolean mFullyInflated;
- private Runnable mOnFinishInflateRunnable;
-
- public UdfpsKeyguardViewLegacy(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- mFingerprintDrawable = new UdfpsFpDrawable(context);
-
- mMaxBurnInOffsetX = context.getResources()
- .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
- mMaxBurnInOffsetY = context.getResources()
- .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
- }
-
- /**
- * Inflate internal udfps view on a background thread and call the onFinishRunnable
- * when inflation is finished.
- */
- public void startIconAsyncInflate(Runnable onFinishInflate) {
- mOnFinishInflateRunnable = onFinishInflate;
- // inflate Lottie views on a background thread in case it takes a while to inflate
- AsyncLayoutInflater inflater = new AsyncLayoutInflater(mContext);
- inflater.inflate(R.layout.udfps_keyguard_view_internal, this,
- mLayoutInflaterFinishListener);
- }
-
- @Override
- public UdfpsDrawable getDrawable() {
- return mFingerprintDrawable;
- }
-
- @Override
- void onSensorRectUpdated(RectF bounds) {
- super.onSensorRectUpdated(bounds);
- bounds.round(this.mSensorBounds);
- postInvalidate();
- }
-
- @Override
- void onDisplayConfiguring() {
- }
-
- @Override
- void onDisplayUnconfigured() {
- }
-
- @Override
- public boolean dozeTimeTick() {
- updateBurnInOffsets();
- return true;
- }
-
- private void updateBurnInOffsets() {
- if (!mFullyInflated) {
- return;
- }
-
- // if we're animating from screen off, we can immediately place the icon in the
- // AoD-burn in location, else we need to translate the icon from LS => AoD.
- final float darkAmountForAnimation = mAnimationType == ANIMATE_APPEAR_ON_SCREEN_OFF
- ? 1f : mInterpolatedDarkAmount;
- final float burnInOffsetX = MathUtils.lerp(0f,
- getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
- - mMaxBurnInOffsetX, darkAmountForAnimation);
- final float burnInOffsetY = MathUtils.lerp(0f,
- getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
- - mMaxBurnInOffsetY, darkAmountForAnimation);
- final float burnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(),
- darkAmountForAnimation);
-
- if (mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN && !mPauseAuth) {
- mLockScreenFp.setTranslationX(burnInOffsetX);
- mLockScreenFp.setTranslationY(burnInOffsetY);
- mBgProtection.setAlpha(1f - mInterpolatedDarkAmount);
- mLockScreenFp.setAlpha(1f - mInterpolatedDarkAmount);
- } else if (darkAmountForAnimation == 0f) {
- // we're on the lockscreen and should use mAlpha (changes based on shade expansion)
- mLockScreenFp.setTranslationX(0);
- mLockScreenFp.setTranslationY(0);
- mBgProtection.setAlpha(mAlpha / 255f);
- mLockScreenFp.setAlpha(mAlpha / 255f);
- } else {
- mBgProtection.setAlpha(0f);
- mLockScreenFp.setAlpha(0f);
- }
- mLockScreenFp.setProgress(1f - mInterpolatedDarkAmount);
-
- mAodFp.setTranslationX(burnInOffsetX);
- mAodFp.setTranslationY(burnInOffsetY);
- mAodFp.setProgress(burnInProgress);
- mAodFp.setAlpha(mInterpolatedDarkAmount);
-
- // done animating
- final boolean doneAnimatingBetweenAodAndLS =
- mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN
- && (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f);
- final boolean doneAnimatingUnlockedScreenOff =
- mAnimationType == ANIMATE_APPEAR_ON_SCREEN_OFF
- && (mInterpolatedDarkAmount == 1f);
- if (doneAnimatingBetweenAodAndLS || doneAnimatingUnlockedScreenOff) {
- mAnimationType = ANIMATION_NONE;
- }
- }
-
- void requestUdfps(boolean request, int color) {
- mUdfpsRequested = request;
- }
-
- void updateColor() {
- if (!mFullyInflated) {
- return;
- }
-
- mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
- com.android.internal.R.attr.materialColorOnSurface);
- final int backgroundColor = Utils.getColorAttrDefaultColor(getContext(),
- com.android.internal.R.attr.materialColorSurfaceContainerHigh);
- mBgProtection.setImageTintList(ColorStateList.valueOf(backgroundColor));
- mLockScreenFp.invalidate(); // updated with a valueCallback
- }
-
- void setScaleFactor(float scale) {
- mScaleFactor = scale;
- }
-
- void updatePadding() {
- if (mLockScreenFp == null || mAodFp == null) {
- return;
- }
-
- final int defaultPaddingPx =
- getResources().getDimensionPixelSize(R.dimen.lock_icon_padding);
- final int padding = (int) (defaultPaddingPx * mScaleFactor);
- mLockScreenFp.setPadding(padding, padding, padding, padding);
- mAodFp.setPadding(padding, padding, padding, padding);
- }
-
- /**
- * @param alpha between 0 and 255
- */
- void setUnpausedAlpha(int alpha) {
- mAlpha = alpha;
- updateAlpha();
- }
-
- /**
- * @return alpha between 0 and 255
- */
- int getUnpausedAlpha() {
- return mAlpha;
- }
-
- @Override
- protected int updateAlpha() {
- int alpha = super.updateAlpha();
- updateBurnInOffsets();
- return alpha;
- }
-
- @Override
- int calculateAlpha() {
- if (mPauseAuth) {
- return 0;
- }
- return mAlpha;
- }
-
- static final int ANIMATION_NONE = 0;
- static final int ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN = 1;
- static final int ANIMATE_APPEAR_ON_SCREEN_OFF = 2;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({ANIMATION_NONE, ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, ANIMATE_APPEAR_ON_SCREEN_OFF})
- private @interface AnimationType {}
-
- void onDozeAmountChanged(float linear, float eased, @AnimationType int animationType) {
- mAnimationType = animationType;
- mInterpolatedDarkAmount = eased;
- updateAlpha();
- }
-
- void updateSensorLocation(@NonNull Rect sensorBounds) {
- mSensorBounds.set(sensorBounds);
- }
-
- /**
- * Animates in the bg protection circle behind the fp icon to highlight the icon.
- */
- void animateInUdfpsBouncer(Runnable onEndAnimation) {
- if (mBackgroundInAnimator.isRunning() || !mFullyInflated) {
- // already animating in or not yet inflated
- return;
- }
-
- // fade in and scale up
- mBackgroundInAnimator = new AnimatorSet();
- mBackgroundInAnimator.playTogether(
- ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f),
- ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f),
- ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f));
- mBackgroundInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mBackgroundInAnimator.setDuration(500);
- mBackgroundInAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (onEndAnimation != null) {
- onEndAnimation.run();
- }
- }
- });
- mBackgroundInAnimator.start();
- }
-
- /**
- * Print debugging information for this class.
- */
- public void dump(PrintWriter pw) {
- pw.println("UdfpsKeyguardView (" + this + ")");
- pw.println(" mPauseAuth=" + mPauseAuth);
- pw.println(" mUnpausedAlpha=" + getUnpausedAlpha());
- pw.println(" mUdfpsRequested=" + mUdfpsRequested);
- pw.println(" mInterpolatedDarkAmount=" + mInterpolatedDarkAmount);
- pw.println(" mAnimationType=" + mAnimationType);
- }
-
- private final AsyncLayoutInflater.OnInflateFinishedListener mLayoutInflaterFinishListener =
- new AsyncLayoutInflater.OnInflateFinishedListener() {
- @Override
- public void onInflateFinished(View view, int resid, ViewGroup parent) {
- mFullyInflated = true;
- mAodFp = view.findViewById(R.id.udfps_aod_fp);
- mLockScreenFp = view.findViewById(R.id.udfps_lockscreen_fp);
- mBgProtection = view.findViewById(R.id.udfps_keyguard_fp_bg);
-
- updatePadding();
- updateColor();
- updateAlpha();
-
- final LayoutParams lp = (LayoutParams) view.getLayoutParams();
- lp.width = mSensorBounds.width();
- lp.height = mSensorBounds.height();
- RectF relativeToView = getBoundsRelativeToView(new RectF(mSensorBounds));
- lp.setMarginsRelative((int) relativeToView.left, (int) relativeToView.top,
- (int) relativeToView.right, (int) relativeToView.bottom);
- parent.addView(view, lp);
-
- // requires call to invalidate to update the color
- mLockScreenFp.addValueCallback(new KeyPath("**"), LottieProperty.COLOR_FILTER,
- frameInfo -> new PorterDuffColorFilter(mTextColorPrimary,
- PorterDuff.Mode.SRC_ATOP));
- mOnFinishInflateRunnable.run();
- }
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
deleted file mode 100644
index 76bcd6e2863b..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.biometrics
-
-import android.content.Context
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Paint
-import android.graphics.Rect
-import android.graphics.RectF
-import android.util.AttributeSet
-import android.util.Log
-import android.view.MotionEvent
-import android.widget.FrameLayout
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
-import com.android.systemui.doze.DozeReceiver
-
-private const val TAG = "UdfpsView"
-
-/**
- * The main view group containing all UDFPS animations.
- */
-class UdfpsView(
- context: Context,
- attrs: AttributeSet?
-) : FrameLayout(context, attrs), DozeReceiver {
- // sensorRect may be bigger than the sensor. True sensor dimensions are defined in
- // overlayParams.sensorBounds
- var sensorRect = Rect()
- private var mUdfpsDisplayMode: UdfpsDisplayModeProvider? = null
- private val debugTextPaint = Paint().apply {
- isAntiAlias = true
- color = Color.BLUE
- textSize = 32f
- }
-
- /** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */
- var animationViewController: UdfpsAnimationViewController<*>? = null
-
- /** Parameters that affect the position and size of the overlay. */
- var overlayParams = UdfpsOverlayParams()
-
- /** Debug message. */
- var debugMessage: String? = null
- set(value) {
- field = value
- postInvalidate()
- }
-
- /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */
- var isDisplayConfigured: Boolean = false
- private set
-
- fun setUdfpsDisplayModeProvider(udfpsDisplayModeProvider: UdfpsDisplayModeProvider?) {
- mUdfpsDisplayMode = udfpsDisplayModeProvider
- }
-
- // Don't propagate any touch events to the child views.
- override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
- return (animationViewController == null || !animationViewController!!.shouldPauseAuth())
- }
-
- override fun dozeTimeTick() {
- animationViewController?.dozeTimeTick()
- }
-
- override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
- super.onLayout(changed, left, top, right, bottom)
-
- // Updates sensor rect in relation to the overlay view
- animationViewController?.onSensorRectUpdated(RectF(sensorRect))
- }
-
- override fun onAttachedToWindow() {
- super.onAttachedToWindow()
- Log.v(TAG, "onAttachedToWindow")
- }
-
- override fun onDetachedFromWindow() {
- super.onDetachedFromWindow()
- Log.v(TAG, "onDetachedFromWindow")
- }
-
- override fun onDraw(canvas: Canvas) {
- super.onDraw(canvas)
- if (!isDisplayConfigured) {
- if (!debugMessage.isNullOrEmpty()) {
- canvas.drawText(debugMessage!!, 0f, 160f, debugTextPaint)
- }
- }
- }
-
- fun configureDisplay(onDisplayConfigured: Runnable) {
- isDisplayConfigured = true
- animationViewController?.onDisplayConfiguring()
- mUdfpsDisplayMode?.enable(onDisplayConfigured)
- }
-
- fun unconfigureDisplay() {
- isDisplayConfigured = false
- animationViewController?.onDisplayUnconfigured()
- mUdfpsDisplayMode?.disable(null /* onDisabled */)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
index 7503a8b4362d..a105d663424c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
@@ -23,7 +23,6 @@ import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay
import com.android.systemui.biometrics.ui.viewmodel.UdfpsTouchOverlayViewModel
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.lifecycle.repeatWhenAttached
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
@@ -42,14 +41,13 @@ object UdfpsTouchOverlayBinder {
viewModel: UdfpsTouchOverlayViewModel,
udfpsOverlayInteractor: UdfpsOverlayInteractor,
) {
- if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) return
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.shouldHandleTouches.collect { shouldHandleTouches ->
Log.d(
"UdfpsTouchOverlayBinder",
- "[$view]: update shouldHandleTouches=$shouldHandleTouches"
+ "[$view]: update shouldHandleTouches=$shouldHandleTouches",
)
view.isInvisible = !shouldHandleTouches
udfpsOverlayInteractor.setHandleTouches(shouldHandleTouches)
@@ -58,7 +56,7 @@ object UdfpsTouchOverlayBinder {
.invokeOnCompletion {
Log.d(
"UdfpsTouchOverlayBinder",
- "[$view-detached]: update shouldHandleTouches=false"
+ "[$view-detached]: update shouldHandleTouches=false",
)
udfpsOverlayInteractor.setHandleTouches(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
index f1c3f949ffba..22b2888a51a9 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
@@ -24,7 +24,6 @@ import com.android.systemui.bouncer.shared.model.BouncerDismissActionModel
import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.log.dagger.BouncerTableLog
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
@@ -88,7 +87,6 @@ interface KeyguardBouncerRepository {
val showMessage: StateFlow<BouncerShowMessageModel?>
val resourceUpdateRequests: StateFlow<Boolean>
val alternateBouncerVisible: StateFlow<Boolean>
- val alternateBouncerUIAvailable: StateFlow<Boolean>
/** Last shown security mode of the primary bouncer (ie: pin/pattern/password/SIM) */
val lastShownSecurityMode: StateFlow<KeyguardSecurityModel.SecurityMode>
@@ -126,8 +124,6 @@ interface KeyguardBouncerRepository {
fun setAlternateVisible(isVisible: Boolean)
- fun setAlternateBouncerUIAvailable(isAvailable: Boolean)
-
fun setLastShownSecurityMode(securityMode: KeyguardSecurityModel.SecurityMode)
}
@@ -199,9 +195,6 @@ constructor(
private val _alternateBouncerVisible = MutableStateFlow(false)
override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow()
override var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE
- private val _alternateBouncerUIAvailable = MutableStateFlow(false)
- override val alternateBouncerUIAvailable: StateFlow<Boolean> =
- _alternateBouncerUIAvailable.asStateFlow()
init {
setUpLogging()
@@ -220,11 +213,6 @@ constructor(
_alternateBouncerVisible.value = isVisible
}
- override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
- DeviceEntryUdfpsRefactor.assertInLegacyMode()
- _alternateBouncerUIAvailable.value = isAvailable
- }
-
override fun setPrimaryShow(isShowing: Boolean) {
_primaryBouncerShow.value = isShowing
}
@@ -320,9 +308,6 @@ constructor(
resourceUpdateRequests
.logDiffsForTable(buffer, "", "ResourceUpdateRequests", false)
.launchIn(applicationScope)
- alternateBouncerUIAvailable
- .logDiffsForTable(buffer, "", "IsAlternateBouncerUIAvailable", false)
- .launchIn(applicationScope)
alternateBouncerVisible
.logDiffsForTable(buffer, "", "AlternateBouncerVisible", false)
.launchIn(applicationScope)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
index 0949ea4d7797..9c2a10a444e2 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
@@ -17,22 +17,17 @@
package com.android.systemui.bouncer.domain.interactor
import android.util.Log
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.time.SystemClock
import dagger.Lazy
@@ -55,13 +50,9 @@ import kotlinx.coroutines.flow.stateIn
class AlternateBouncerInteractor
@Inject
constructor(
- private val statusBarStateController: StatusBarStateController,
- private val keyguardStateController: KeyguardStateController,
private val bouncerRepository: KeyguardBouncerRepository,
fingerprintPropertyRepository: FingerprintPropertyRepository,
- private val biometricSettingsRepository: BiometricSettingsRepository,
private val systemClock: SystemClock,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val deviceEntryBiometricsAllowedInteractor:
Lazy<DeviceEntryBiometricsAllowedInteractor>,
private val keyguardInteractor: Lazy<KeyguardInteractor>,
@@ -73,17 +64,10 @@ constructor(
val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
private val alternateBouncerUiAvailableFromSource: HashSet<String> = HashSet()
val alternateBouncerSupported: StateFlow<Boolean> =
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- fingerprintPropertyRepository.sensorType
- .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() }
- .stateIn(
- scope = scope,
- started = SharingStarted.Eagerly,
- initialValue = false,
- )
- } else {
- bouncerRepository.alternateBouncerUIAvailable
- }
+ fingerprintPropertyRepository.sensorType
+ .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() }
+ .stateIn(scope = scope, started = SharingStarted.Eagerly, initialValue = false)
+
private val isDozingOrAod: Flow<Boolean> =
anyOf(
keyguardTransitionInteractor.get().transitionValue(KeyguardState.DOZING).map {
@@ -106,7 +90,7 @@ constructor(
combine(
keyguardTransitionInteractor.get().currentKeyguardState,
sceneInteractor.get().currentScene,
- ::Pair
+ ::Pair,
)
.flatMapLatest { (currentKeyguardState, transitionState) ->
if (currentKeyguardState == KeyguardState.GONE) {
@@ -122,7 +106,7 @@ constructor(
.isFingerprintAuthCurrentlyAllowed,
keyguardInteractor.get().isKeyguardDismissible,
bouncerRepository.primaryBouncerShow,
- isDozingOrAod
+ isDozingOrAod,
) {
fingerprintAllowed,
keyguardDismissible,
@@ -141,37 +125,17 @@ constructor(
}
.distinctUntilChanged()
.onEach { Log.d(TAG, "canShowAlternateBouncer changed to $it") }
- .stateIn(
- scope = scope,
- started = WhileSubscribed(),
- initialValue = false,
- )
+ .stateIn(scope = scope, started = WhileSubscribed(), initialValue = false)
/**
* Always shows the alternate bouncer. Requesters must check [canShowAlternateBouncer]` before
* calling this.
*/
fun forceShow() {
- if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
- show()
- return
- }
bouncerRepository.setAlternateVisible(true)
}
/**
- * Sets the correct bouncer states to show the alternate bouncer if it can show.
- *
- * @return whether alternateBouncer is visible
- * @deprecated use [forceShow] and manually check [canShowAlternateBouncer] beforehand
- */
- fun show(): Boolean {
- DeviceEntryUdfpsRefactor.assertInLegacyMode()
- bouncerRepository.setAlternateVisible(canShowAlternateBouncerForFingerprint())
- return isVisibleState()
- }
-
- /**
* Sets the correct bouncer states to hide the bouncer. Should only be called through
* StatusBarKeyguardViewManager until ScrimController is refactored to use
* alternateBouncerInteractor.
@@ -189,28 +153,8 @@ constructor(
return bouncerRepository.alternateBouncerVisible.value
}
- fun setAlternateBouncerUIAvailable(isAvailable: Boolean, token: String) {
- DeviceEntryUdfpsRefactor.assertInLegacyMode()
- if (isAvailable) {
- alternateBouncerUiAvailableFromSource.add(token)
- } else {
- alternateBouncerUiAvailableFromSource.remove(token)
- }
- bouncerRepository.setAlternateBouncerUIAvailable(
- alternateBouncerUiAvailableFromSource.isNotEmpty()
- )
- }
-
fun canShowAlternateBouncerForFingerprint(): Boolean {
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- return canShowAlternateBouncer.value
- }
- return alternateBouncerSupported.value &&
- biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value &&
- !keyguardUpdateMonitor.isFingerprintLockedOut &&
- !keyguardStateController.isUnlocked &&
- !statusBarStateController.isDozing &&
- !bouncerRepository.primaryBouncerShow.value
+ return canShowAlternateBouncer.value
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
index 2c026c0bb5ce..7337e5af51a1 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
@@ -30,6 +30,7 @@ import com.android.internal.util.EmergencyAffordanceManager
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.data.repository.EmergencyServicesRepository
import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
+import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -74,6 +75,7 @@ constructor(
private val metricsLogger: MetricsLogger,
private val dozeLogger: DozeLogger,
private val sceneInteractor: Lazy<SceneInteractor>,
+ private val bouncerHapticPlayer: BouncerHapticPlayer,
) {
/** The bouncer action button. If `null`, the button should not be shown. */
val actionButton: Flow<BouncerActionButtonModel?> =
@@ -111,6 +113,8 @@ constructor(
BouncerActionButtonModel(
label = applicationContext.getString(R.string.lockscreen_emergency_call),
onClick = {
+ // TODO(b/373930432): haptics should be played at the UI layer -> refactor
+ bouncerHapticPlayer.playEmergencyButtonClickFeedback()
prepareToPerformAction()
dozeLogger.logEmergencyCall()
startEmergencyDialerActivity()
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt
index d6b92115c64b..837390730c7a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerHapticPlayer.kt
@@ -81,4 +81,11 @@ class BouncerHapticPlayer @Inject constructor(private val msdlPlayer: dagger.Laz
/** Deliver MSDL feedback when a numpad key is pressed on the pin bouncer */
fun playNumpadKeyFeedback() = msdlPlayer.get().playToken(MSDLToken.KEYPRESS_STANDARD)
+
+ /** Deliver MSDL feedback when clicking on the emergency button */
+ fun playEmergencyButtonClickFeedback() {
+ if (isEnabled) {
+ msdlPlayer.get().playToken(MSDLToken.KEYPRESS_RETURN)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 4185aed3095c..148b9ea61e2c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.bouncer.ui.viewmodel
import android.annotation.StringRes
-import com.android.app.tracing.coroutines.flow.collectLatest
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
@@ -28,6 +27,7 @@ import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.receiveAsFlow
sealed class AuthMethodBouncerViewModel(
diff --git a/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt
index e3f1174d4a5f..a695163ed155 100644
--- a/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt
@@ -19,7 +19,7 @@ package com.android.systemui.common.usagestats.data.repository
import android.app.usage.UsageEvents
import android.app.usage.UsageEventsQuery
import android.app.usage.UsageStatsManager
-import com.android.app.tracing.coroutines.withContext
+import com.android.app.tracing.coroutines.withContextTraced as withContext
import com.android.systemui.common.usagestats.data.model.UsageStatsQuery
import com.android.systemui.common.usagestats.shared.model.ActivityEventModel
import com.android.systemui.common.usagestats.shared.model.ActivityEventModel.Lifecycle
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index 08a7c395e57f..8510915d55af 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal
+import android.os.UserHandle
import android.provider.Settings
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
@@ -147,9 +148,10 @@ constructor(
.emitOnStart()
.onEach {
screenTimeout =
- systemSettings.getInt(
+ systemSettings.getIntForUser(
Settings.System.SCREEN_OFF_TIMEOUT,
DEFAULT_SCREEN_TIMEOUT,
+ UserHandle.USER_CURRENT,
)
}
.launchIn(bgScope)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 3a04d026ee84..dc248059b3d4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -23,7 +23,7 @@ import android.content.pm.UserInfo
import android.os.UserHandle
import android.os.UserManager
import android.provider.Settings
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index 3826fb40ea3d..428b83ddbb3a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -16,7 +16,7 @@
package com.android.systemui.communal.domain.interactor
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
index ba96f4e56421..71bfe0c057e4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
@@ -24,8 +24,7 @@ import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.compose.ui.unit.IntSize
import androidx.core.view.doOnLayout
-import com.android.app.tracing.coroutines.flow.flowOn
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags.communalWidgetResizing
import com.android.systemui.common.ui.view.onLayoutChanged
import com.android.systemui.communal.domain.model.CommunalContentModel
@@ -41,6 +40,7 @@ import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
object CommunalAppWidgetHostViewBinder {
private const val TAG = "CommunalAppWidgetHostViewBinder"
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt
index 87fcdd7b97ee..a519649a8f1c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt
@@ -19,7 +19,7 @@ package com.android.systemui.communal.ui.viewmodel
import androidx.compose.foundation.gestures.AnchoredDraggableState
import androidx.compose.foundation.gestures.DraggableAnchors
import androidx.compose.runtime.snapshotFlow
-import com.android.app.tracing.coroutines.coroutineScope
+import com.android.app.tracing.coroutines.coroutineScopeTraced as coroutineScope
import com.android.systemui.lifecycle.ExclusiveActivatable
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.Flow
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
index 07a7c7cba2fd..d5d3a927181b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
@@ -19,7 +19,7 @@ package com.android.systemui.communal.util
import android.content.Context
import android.os.Bundle
import android.util.SizeF
-import com.android.app.tracing.coroutines.withContext
+import com.android.app.tracing.coroutines.withContextTraced as withContext
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.widgets.AppWidgetHostListenerDelegate
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
index 542b98896986..c894267a61dd 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -21,7 +21,7 @@ import android.app.PendingIntent
import android.content.Intent
import android.view.View
import android.widget.RemoteViews
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.Flags.communalWidgetTrampolineFix
import com.android.systemui.animation.ActivityTransitionAnimator
diff --git a/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt b/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt
new file mode 100644
index 000000000000..5b1c9c8b8020
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.systemui.coroutines
+
+import com.android.app.tracing.coroutines.createCoroutineTracingContext
+import kotlin.coroutines.CoroutineContext
+
+fun newTracingContext(name: String): CoroutineContext {
+ return createCoroutineTracingContext(name) { className ->
+ className.startsWith("com.android.systemui.util.kotlin.JavaAdapter") ||
+ className.startsWith("com.android.systemui.lifecycle.RepeatWhenAttached")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
index b8c03c071572..c464a66ea0c3 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt
@@ -17,25 +17,17 @@
package com.android.systemui.deviceentry
import com.android.keyguard.EmptyLockIconViewController
-import com.android.keyguard.LegacyLockIconViewController
import com.android.keyguard.LockIconViewController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule
import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.multibindings.Multibinds
-@Module(
- includes =
- [
- DeviceEntryRepositoryModule::class,
- FaceWakeUpTriggersConfigModule::class,
- ],
-)
+@Module(includes = [DeviceEntryRepositoryModule::class, FaceWakeUpTriggersConfigModule::class])
abstract class DeviceEntryModule {
/**
* A set of DeviceEntryIconTransitions. Ensures that this can be injected even if it's empty.
@@ -46,14 +38,9 @@ abstract class DeviceEntryModule {
@Provides
@SysUISingleton
fun provideLockIconViewController(
- legacyLockIconViewController: Lazy<LegacyLockIconViewController>,
- emptyLockIconViewController: Lazy<EmptyLockIconViewController>,
+ emptyLockIconViewController: Lazy<EmptyLockIconViewController>
): LockIconViewController {
- return if (DeviceEntryUdfpsRefactor.isEnabled) {
- emptyLockIconViewController.get()
- } else {
- legacyLockIconViewController.get()
- }
+ return emptyLockIconViewController.get()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
index ffe392a52c9f..88daa5de8816 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
@@ -21,7 +21,6 @@ import android.hardware.face.FaceManager
import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
import javax.inject.Inject
@@ -55,9 +54,7 @@ constructor(
faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>()
init {
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- startUpdatingFaceHelpMessageDeferral()
- }
+ startUpdatingFaceHelpMessageDeferral()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
deleted file mode 100644
index b5d5803ca6fb..000000000000
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.deviceentry.shared
-
-import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the device entry udfps refactor flag state. */
-@Suppress("NOTHING_TO_INLINE")
-object DeviceEntryUdfpsRefactor {
- /** The aconfig flag name */
- const val FLAG_NAME = Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
-
- /** A token used for dependency declaration */
- val token: FlagToken
- get() = FlagToken(FLAG_NAME, isEnabled)
-
- /** Is the refactor enabled */
- @JvmStatic
- inline val isEnabled
- get() = Flags.deviceEntryUdfpsRefactor()
-
- /**
- * Called to ensure code is only run when the flag is enabled. This protects users from the
- * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
- * build to ensure that the refactor author catches issues in testing.
- */
- @JvmStatic
- inline fun isUnexpectedlyInLegacyMode() =
- RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
-
- /**
- * Called to ensure code is only run when the flag is disabled. This will throw an exception if
- * the flag is enabled to ensure that the refactor author catches issues in testing.
- */
- @JvmStatic
- inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt
index 30624757ebe3..e3fce0007f26 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt
@@ -17,8 +17,8 @@
package com.android.systemui.display.data.repository
import android.view.Display
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
import com.android.systemui.CoreStartable
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
@@ -69,7 +69,7 @@ constructor(
backgroundApplicationScope
} else {
CoroutineScope(
- backgroundDispatcher + createCoroutineTracingContext("DisplayScope$displayId")
+ backgroundDispatcher + newTracingContext("DisplayScope$displayId")
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt
new file mode 100644
index 000000000000..2ce3e43389fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.systemui.display.data.repository
+
+import android.view.Display
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.qualifiers.Background
+import java.io.PrintWriter
+import java.util.concurrent.ConcurrentHashMap
+import kotlinx.coroutines.CoroutineName
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/** Provides per display instances of [T]. */
+interface PerDisplayStore<T> {
+
+ /**
+ * The instance for the default/main display of the device. For example, on a phone or a tablet,
+ * the default display is the internal/built-in display of the device.
+ *
+ * Note that the id of the default display is [Display.DEFAULT_DISPLAY].
+ */
+ val defaultDisplay: T
+
+ /**
+ * Returns an instance for a specific display id.
+ *
+ * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing
+ * displays.
+ */
+ fun forDisplay(displayId: Int): T
+}
+
+abstract class PerDisplayStoreImpl<T>(
+ @Background private val backgroundApplicationScope: CoroutineScope,
+ private val displayRepository: DisplayRepository,
+) : PerDisplayStore<T>, CoreStartable {
+
+ private val perDisplayInstances = ConcurrentHashMap<Int, T>()
+
+ /**
+ * The instance for the default/main display of the device. For example, on a phone or a tablet,
+ * the default display is the internal/built-in display of the device.
+ *
+ * Note that the id of the default display is [Display.DEFAULT_DISPLAY].
+ */
+ override val defaultDisplay: T
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ /**
+ * Returns an instance for a specific display id.
+ *
+ * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing
+ * displays.
+ */
+ override fun forDisplay(displayId: Int): T {
+ if (displayRepository.getDisplay(displayId) == null) {
+ throw IllegalArgumentException("Display with id $displayId doesn't exist.")
+ }
+ return perDisplayInstances.computeIfAbsent(displayId) {
+ createInstanceForDisplay(displayId)
+ }
+ }
+
+ abstract fun createInstanceForDisplay(displayId: Int): T
+
+ override fun start() {
+ val instanceType = instanceClass.simpleName
+ backgroundApplicationScope.launch(CoroutineName("PerDisplayStore#<$instanceType>start")) {
+ displayRepository.displayRemovalEvent.collect { removedDisplayId ->
+ val removedInstance = perDisplayInstances.remove(removedDisplayId)
+ removedInstance?.let { onDisplayRemovalAction(it) }
+ }
+ }
+ }
+
+ abstract val instanceClass: Class<T>
+
+ /**
+ * Will be called when the display associated with [instance] was removed. It allows to perform
+ * any clean up if needed.
+ */
+ open suspend fun onDisplayRemovalAction(instance: T) {}
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println(perDisplayInstances)
+ }
+}
+
+class SingleDisplayStore<T>(defaultInstance: T) : PerDisplayStore<T> {
+ override val defaultDisplay: T = defaultInstance
+
+ override fun forDisplay(displayId: Int): T = defaultDisplay
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
index 3992c3fb70b0..724f1c5323cf 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
@@ -22,6 +22,7 @@ import android.service.controls.ControlsProviderService
import android.service.dreams.DreamService
import android.window.TaskFragmentInfo
import com.android.systemui.controls.settings.ControlsSettingsRepository
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dreams.DreamLogger
import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor
@@ -39,7 +40,6 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
class HomeControlsDreamService
@Inject
@@ -54,7 +54,8 @@ constructor(
) : DreamService() {
private val serviceJob = SupervisorJob()
- private val serviceScope = CoroutineScope(bgDispatcher + serviceJob + createCoroutineTracingContext("HomeControlsDreamService"))
+ private val serviceScope =
+ CoroutineScope(bgDispatcher + serviceJob + newTracingContext("HomeControlsDreamService"))
private val logger = DreamLogger(logBuffer, TAG)
private lateinit var taskFragmentComponent: TaskFragmentComponent
private val wakeLock: WakeLock by lazy {
diff --git a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
index 4caf95b707b1..7fa7da192ad0 100644
--- a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
@@ -16,10 +16,10 @@
package com.android.systemui.education.dagger
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
import com.android.systemui.CoreStartable
import com.android.systemui.Flags
import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.education.data.repository.ContextualEducationRepository
import com.android.systemui.education.data.repository.UserContextualEducationRepository
@@ -57,7 +57,9 @@ interface ContextualEducationModule {
fun provideEduDataStoreScope(
@Background bgDispatcher: CoroutineDispatcher
): CoroutineScope {
- return CoroutineScope(bgDispatcher + SupervisorJob() + createCoroutineTracingContext("EduDataStoreScope"))
+ return CoroutineScope(
+ bgDispatcher + SupervisorJob() + newTracingContext("EduDataStoreScope")
+ )
}
@EduClock
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt
index e396767ecf8e..1dbcb3dfe399 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt
@@ -22,7 +22,7 @@ import androidx.compose.foundation.interaction.InteractionSource
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.unit.Velocity
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig
import com.android.systemui.haptics.slider.SliderDragVelocityProvider
import com.android.systemui.haptics.slider.SliderEventType
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index 6df8355550a0..a94df091230d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -30,7 +30,7 @@ import android.net.Uri
import android.os.Binder
import android.os.Bundle
import android.util.Log
-import com.android.app.tracing.coroutines.runBlocking
+import com.android.app.tracing.coroutines.runBlockingTraced as runBlocking
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 690ae71aa56e..b7d0d453779f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -24,7 +24,7 @@ import android.annotation.SuppressLint
import android.os.Trace
import android.util.Log
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.withContext
+import com.android.app.tracing.coroutines.withContextTraced as withContext
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.shared.model.KeyguardState
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
index 96260770d89f..03cf1a4b3e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
@@ -23,15 +23,12 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.data.repository.BiometricType
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -41,7 +38,6 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
/**
* Encapsulates business logic for device entry events that impact the side fingerprint sensor
@@ -51,7 +47,6 @@ import kotlinx.coroutines.launch
class DeviceEntrySideFpsOverlayInteractor
@Inject
constructor(
- @Application private val applicationScope: CoroutineScope,
@Application private val context: Context,
deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
private val sceneInteractor: SceneInteractor,
@@ -60,18 +55,6 @@ constructor(
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
) {
- init {
- if (!DeviceEntryUdfpsRefactor.isEnabled) {
- applicationScope.launch {
- deviceEntryFingerprintAuthRepository.availableFpSensorType.collect { sensorType ->
- if (sensorType == BiometricType.SIDE_FINGERPRINT) {
- alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG)
- }
- }
- }
- }
- }
-
private val isSideFpsIndicatorOnPrimaryBouncerEnabled: Boolean
get() = context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer)
@@ -90,7 +73,7 @@ constructor(
primaryBouncerInteractor.startingDisappearAnimation.filterNotNull(),
// Bouncer scene visibility changes.
isBouncerSceneActive,
- deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it }
+ deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it },
)
.map {
isBouncerActive() &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 4cf9ec8890d4..9896365abff9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -19,7 +19,7 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import android.util.Log
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 9a0a85823929..7b757657b1d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -20,7 +20,7 @@ import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.app.DreamManager
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index 6b6a3dce630a..a6f0db5a86aa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -18,7 +18,7 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index 58c8a0456241..606a7a988970 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -18,7 +18,7 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 6d1d9cbd9aae..4d3727657f3e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -19,7 +19,7 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import android.util.MathUtils
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags.communalSceneKtfRefactor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 2c3b481b9e16..26bf26b34a8a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -22,7 +22,7 @@ import android.app.admin.DevicePolicyManager
import android.content.Context
import android.content.Intent
import android.util.Log
-import com.android.app.tracing.coroutines.withContext
+import com.android.app.tracing.coroutines.withContextTraced as withContext
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt
index fe5f632c0b6a..23b7b664d4f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt
@@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.binder
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.AuthKeyguardMessageArea
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
index 7ca2c2004452..6ef9863f1112 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt
@@ -21,7 +21,7 @@ import android.content.res.ColorStateList
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 1891af2d04b0..7a368999ecc9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -29,7 +29,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index a3f3342b9a6c..6c104a0a2470 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -28,7 +28,7 @@ import androidx.compose.ui.graphics.toArgb
import androidx.core.view.isInvisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.common.ui.view.LongPressHandlingView
import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index 0470e086ad27..5bad0168fe05 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -22,7 +22,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 660a650fb916..3bdf7dac75b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -36,7 +36,7 @@ import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.settingslib.Utils
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.Expandable
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index ba94f45528c7..8b947a3bcb1e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -22,7 +22,7 @@ import android.view.ViewGroup
import android.widget.TextView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
index 76d3389c25b4..2fd9818a38f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt
@@ -22,7 +22,7 @@ import android.view.accessibility.AccessibilityNodeInfo
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.common.ui.view.LongPressHandlingView
import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index 2d225562081a..21d1db4a5052 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -33,7 +33,7 @@ import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.policy.SystemBarUtils
import com.android.systemui.customization.R as customR
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
@@ -129,15 +129,19 @@ object KeyguardPreviewClockViewBinder {
constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0)
val largeClockTopMargin =
SystemBarUtils.getStatusBarHeight(context) +
- context.resources.getDimensionPixelSize(
- customR.dimen.small_clock_padding_top
- ) +
+ context.resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) +
context.resources.getDimensionPixelSize(
R.dimen.keyguard_smartspace_top_offset
) +
getDimen(context, DATE_WEATHER_VIEW_HEIGHT) +
getDimen(context, ENHANCED_SMARTSPACE_HEIGHT)
- connect(customR.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin)
+ connect(
+ customR.id.lockscreen_clock_view_large,
+ TOP,
+ PARENT_ID,
+ TOP,
+ largeClockTopMargin,
+ )
connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START)
connect(
customR.id.lockscreen_clock_view_large,
@@ -146,12 +150,11 @@ object KeyguardPreviewClockViewBinder {
ConstraintSet.END,
)
-
// In preview, we'll show UDFPS icon for UDFPS devices and nothing for non-UDFPS
// devices, but we need position of device entry icon to constrain clock
if (getConstraint(lockId) != null) {
connect(customR.id.lockscreen_clock_view_large, BOTTOM, lockId, TOP)
- } else {
+ } else {
// Copied calculation codes from applyConstraints in DefaultDeviceEntrySection
val bottomPaddingPx =
context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
index 4b75b804e52b..baa681282a0b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt
@@ -22,7 +22,7 @@ import android.view.View
import androidx.core.view.isInvisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.keyguard.shared.model.ClockSizeSetting
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 27dd18d2b24e..cfd6481234ed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -30,7 +30,7 @@ import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
import com.android.settingslib.Utils
import com.android.systemui.animation.Expandable
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index ea70fd02eed5..89bca0c6a373 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -39,7 +39,7 @@ import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
import com.android.keyguard.AuthInteractionProperties
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
index 4150ceb8aa31..79360370d937 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt
@@ -22,7 +22,7 @@ import android.view.View
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.common.ui.binder.TextViewBinder
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
index 8b74f5dc791d..de4a1b03203c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
@@ -21,7 +21,7 @@ import androidx.constraintlayout.helper.widget.Layer
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt
index fd27dc39299b..3934917d13c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt
@@ -16,7 +16,7 @@
package com.android.systemui.keyguard.ui.binder
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSurfaceBehindViewModel
import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
index b2ee68967878..2df17c39d90d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
@@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.binder
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.LightRevealScrim
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt
index ae46dd3a6ef3..b1ce47ee79e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt
@@ -16,7 +16,7 @@
package com.android.systemui.keyguard.ui.binder
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
import com.android.systemui.keyguard.ui.viewmodel.WindowManagerLockscreenVisibilityViewModel
import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index dd8980dabb23..08d35a72ab12 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -47,7 +47,6 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.core.view.isInvisible
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
import com.android.internal.policy.SystemBarUtils
import com.android.keyguard.ClockEventController
import com.android.keyguard.KeyguardClockSwitch
@@ -57,6 +56,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.communal.ui.binder.CommunalTutorialIndicatorViewBinder
import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -189,7 +189,7 @@ constructor(
CoroutineScope(
applicationScope.coroutineContext +
Job() +
- createCoroutineTracingContext("KeyguardPreviewRenderer")
+ newTracingContext("KeyguardPreviewRenderer")
)
disposables += DisposableHandle { coroutineScope.cancel() }
clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData())
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
index 075a1d2893f7..9355200daec7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt
@@ -25,7 +25,7 @@ import android.os.Messenger
import android.util.ArrayMap
import android.util.Log
import androidx.annotation.VisibleForTesting
-import com.android.app.tracing.coroutines.runBlocking
+import com.android.app.tracing.coroutines.runBlockingTraced as runBlocking
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index 0d55709e94d6..f765e60c1c70 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel
import androidx.annotation.VisibleForTesting
import com.android.app.tracing.FlowTracing.traceEmissionCount
+import com.android.app.tracing.coroutines.flow.flowName
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
@@ -141,6 +142,7 @@ constructor(
fadeInAlpha,
fadeOutAlpha,
)
+ .flowName("transitionAlpha")
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt
index df1394bbfa5f..7c02f28c0696 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt
@@ -19,7 +19,7 @@ package com.android.systemui.lifecycle
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.snapshots.StateFactoryMarker
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.coroutines.traceCoroutine
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
index 555969859a1f..a86bfb18fb70 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
@@ -17,7 +17,6 @@
package com.android.systemui.lifecycle
-import android.os.Trace
import android.view.View
import android.view.ViewTreeObserver
import androidx.annotation.MainThread
@@ -25,11 +24,8 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.lifecycleScope
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
-import com.android.app.tracing.coroutines.traceCoroutine
-import com.android.systemui.Flags.coroutineTracing
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.util.Assert
-import com.android.systemui.util.Compile
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import kotlin.coroutines.CoroutineContext
@@ -83,25 +79,13 @@ fun View.repeatWhenAttached(
// default behavior. Instead, we want it to run on the view's UI thread since the user will
// presumably want to call view methods that require being called from said UI thread.
val lifecycleCoroutineContext = MAIN_DISPATCHER_SINGLETON + coroutineContext
- val traceName =
- if (Compile.IS_DEBUG && coroutineTracing()) {
- inferTraceSectionName()
- } else {
- DEFAULT_TRACE_NAME
- }
var lifecycleOwner: ViewLifecycleOwner? = null
val onAttachListener =
object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
Assert.isMainThread()
lifecycleOwner?.onDestroy()
- lifecycleOwner =
- createLifecycleOwnerAndRun(
- traceName,
- view,
- lifecycleCoroutineContext,
- block,
- )
+ lifecycleOwner = createLifecycleOwnerAndRun(view, lifecycleCoroutineContext, block)
}
override fun onViewDetachedFromWindow(v: View) {
@@ -112,13 +96,7 @@ fun View.repeatWhenAttached(
addOnAttachStateChangeListener(onAttachListener)
if (view.isAttachedToWindow) {
- lifecycleOwner =
- createLifecycleOwnerAndRun(
- traceName,
- view,
- lifecycleCoroutineContext,
- block,
- )
+ lifecycleOwner = createLifecycleOwnerAndRun(view, lifecycleCoroutineContext, block)
}
return DisposableHandle {
@@ -131,14 +109,15 @@ fun View.repeatWhenAttached(
}
private fun createLifecycleOwnerAndRun(
- nameForTrace: String,
view: View,
coroutineContext: CoroutineContext,
block: suspend LifecycleOwner.(View) -> Unit,
): ViewLifecycleOwner {
return ViewLifecycleOwner(view).apply {
onCreate()
- lifecycleScope.launch(coroutineContext) { traceCoroutine(nameForTrace) { block(view) } }
+ // TODO(b/370595466): Refactor to support installing CoroutineTracingContext on the
+ // top-level CoroutineScope used as the lifecycleScope
+ lifecycleScope.launch(coroutineContext) { block(view) }
}
}
@@ -167,9 +146,7 @@ private fun createLifecycleOwnerAndRun(
* └───────────────┴───────────────────┴──────────────┴─────────────────┘
* ```
*/
-class ViewLifecycleOwner(
- private val view: View,
-) : LifecycleOwner {
+class ViewLifecycleOwner(private val view: View) : LifecycleOwner {
private val windowVisibleListener =
ViewTreeObserver.OnWindowVisibilityChangeListener { updateState() }
@@ -205,28 +182,6 @@ class ViewLifecycleOwner(
}
}
-private fun isFrameInteresting(frame: StackWalker.StackFrame): Boolean =
- frame.className != CURRENT_CLASS_NAME && frame.className != JAVA_ADAPTER_CLASS_NAME
-
-/** Get a name for the trace section include the name of the call site. */
-private fun inferTraceSectionName(): String {
- try {
- Trace.traceBegin(Trace.TRACE_TAG_APP, "RepeatWhenAttachedKt#inferTraceSectionName")
- val interestingFrame =
- StackWalker.getInstance().walk { stream ->
- stream.filter(::isFrameInteresting).limit(5).findFirst()
- }
- return if (interestingFrame.isPresent) {
- val f = interestingFrame.get()
- "${f.className}#${f.methodName}:${f.lineNumber} [$DEFAULT_TRACE_NAME]"
- } else {
- DEFAULT_TRACE_NAME
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_APP)
- }
-}
-
/**
* Runs the given [block] in a new coroutine when `this` [View]'s Window's [WindowLifecycleState] is
* at least at [state] (or immediately after calling this function if the window is already at least
@@ -368,8 +323,7 @@ private val ViewTreeObserver.isWindowVisible
* an extension function, and plumbing dagger-injected instances for static usage has little
* benefit.
*/
-private val MAIN_DISPATCHER_SINGLETON =
- Dispatchers.Main + createCoroutineTracingContext("RepeatWhenAttached")
+private val MAIN_DISPATCHER_SINGLETON = Dispatchers.Main + newTracingContext("RepeatWhenAttached")
private const val DEFAULT_TRACE_NAME = "repeatWhenAttached"
private const val CURRENT_CLASS_NAME = "com.android.systemui.lifecycle.RepeatWhenAttachedKt"
private const val JAVA_ADAPTER_CLASS_NAME = "com.android.systemui.util.kotlin.JavaAdapterKt"
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 544dbddeb3f0..f40ad065e5fc 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -16,13 +16,13 @@
package com.android.systemui.mediaprojection.appselector
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.os.UserHandle
import androidx.lifecycle.DefaultLifecycleObserver
import com.android.launcher3.icons.IconFactory
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
@@ -134,7 +134,11 @@ interface MediaProjectionAppSelectorModule {
@MediaProjectionAppSelector
@MediaProjectionAppSelectorScope
fun provideCoroutineScope(@Application applicationScope: CoroutineScope): CoroutineScope =
- CoroutineScope(applicationScope.coroutineContext + SupervisorJob() + createCoroutineTracingContext("MediaProjectionAppSelectorScope"))
+ CoroutineScope(
+ applicationScope.coroutineContext +
+ SupervisorJob() +
+ newTracingContext("MediaProjectionAppSelectorScope")
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index c3729c0dcdfd..212da9ffb9c5 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -122,8 +122,11 @@ public class MediaProjectionPermissionActivity extends Activity {
final Intent launchingIntent = getIntent();
mReviewGrantedConsentRequired = launchingIntent.getBooleanExtra(
EXTRA_USER_REVIEW_GRANTED_CONSENT, false);
-
- mPackageName = getCallingPackage();
+ if (com.android.systemui.Flags.mediaProjectionRequestAttributionFix()) {
+ mPackageName = getLaunchedFromPackage();
+ } else {
+ mPackageName = getCallingPackage();
+ }
// This activity is launched directly by an app, or system server. System server provides
// the package name through the intent if so.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt
index 96386e520d5a..0166176721c3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt
@@ -16,7 +16,6 @@
package com.android.systemui.navigationbar.gestural.domain
-import com.android.app.tracing.coroutines.flow.flowOn
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -35,6 +34,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 3aa9daac4866..d0f6f7961889 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -37,7 +37,7 @@ import android.os.UserManager
import android.provider.Settings
import android.widget.Toast
import androidx.annotation.VisibleForTesting
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index 9c5231d716da..49b44cb95e46 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -23,7 +23,9 @@ import android.graphics.Rect
import android.os.Bundle
import android.util.IndentingPrintWriter
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.View
+import android.view.ViewConfiguration
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.activity.OnBackPressedDispatcher
@@ -35,6 +37,7 @@ import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
@@ -43,6 +46,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
@@ -83,6 +87,7 @@ import com.android.systemui.Dumpable
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dump.DumpManager
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.setSnapshotBinding
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.dagger.MediaModule.QS_PANEL
@@ -145,6 +150,7 @@ constructor(
private val qqsVisible = MutableStateFlow(false)
private val qqsPositionOnRoot = Rect()
private val composeViewPositionOnScreen = Rect()
+ private val scrollState = ScrollState(0)
// Inside object for namespacing
private val notificationScrimClippingParams =
@@ -210,6 +216,9 @@ constructor(
context,
{ notificationScrimClippingParams.isEnabled },
{ notificationScrimClippingParams.params.top },
+ // Only allow scrolling when we are fully expanded. That way, we don't intercept
+ // swipes in lockscreen (when somehow QS is receiving touches).
+ { scrollState.canScrollForward && viewModel.expansionState.value.progress >= 1f },
)
frame.addView(
composeView,
@@ -488,12 +497,8 @@ constructor(
private fun setListenerCollections() {
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
- launch {
- // TODO
- // setListenerJob(
- // scrollListener,
- //
- // )
+ this@QSFragmentCompose.view?.setSnapshotBinding {
+ scrollListener.value?.onQsPanelScrollChanged(scrollState.value)
}
launch {
setListenerJob(
@@ -528,6 +533,7 @@ constructor(
viewModel.containerViewModel.quickQuickSettingsViewModel.squishinessViewModel
.squishiness
.collectAsStateWithLifecycle()
+
Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) {
Box(
modifier =
@@ -591,7 +597,12 @@ constructor(
modifier =
Modifier.element(ElementKeys.QuickSettingsContent).fillMaxSize().weight(1f)
) {
- Column {
+ DisposableEffect(Unit) {
+ lifecycleScope.launch { scrollState.scrollTo(0) }
+ onDispose { lifecycleScope.launch { scrollState.scrollTo(0) } }
+ }
+
+ Column(modifier = Modifier.verticalScroll(scrollState)) {
Spacer(
modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() }
)
@@ -601,15 +612,14 @@ constructor(
)
}
}
- QuickSettingsTheme {
- FooterActions(
- viewModel = viewModel.footerActionsViewModel,
- qsVisibilityLifecycleOwner = this@QSFragmentCompose,
- modifier =
- Modifier.sysuiResTag("qs_footer_actions")
- .element(ElementKeys.FooterActions),
- )
- }
+ }
+ QuickSettingsTheme {
+ FooterActions(
+ viewModel = viewModel.footerActionsViewModel,
+ qsVisibilityLifecycleOwner = this@QSFragmentCompose,
+ modifier =
+ Modifier.sysuiResTag("qs_footer_actions").element(ElementKeys.FooterActions),
+ )
}
}
}
@@ -791,13 +801,17 @@ private class ExpansionTransition(currentProgress: Float) :
private const val EDIT_MODE_TIME_MILLIS = 500
/**
- * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as
- * per [clippingEnabledProvider].
+ * Performs different touch handling based on the state of the ComposeView:
+ * * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as
+ * per [clippingEnabledProvider].
+ * * Intercept touches that would overscroll QS forward and instead allow them to be used to close
+ * the shade.
*/
private class FrameLayoutTouchPassthrough(
context: Context,
private val clippingEnabledProvider: () -> Boolean,
private val clippingTopProvider: () -> Int,
+ private val canScrollForwardQs: () -> Boolean,
) : FrameLayout(context) {
override fun isTransformedTouchPointInView(
x: Float,
@@ -811,4 +825,32 @@ private class FrameLayoutTouchPassthrough(
super.isTransformedTouchPointInView(x, y, child, outLocalPoint)
}
}
+
+ val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
+ var downY = 0f
+
+ override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
+ // If there's a touch on this view and we can scroll down, we don't want to be intercepted
+ val action = ev.actionMasked
+
+ when (action) {
+ MotionEvent.ACTION_DOWN -> {
+ // If we can scroll down, make sure none of our parents intercepts us.
+ if (canScrollForwardQs()) {
+ parent?.requestDisallowInterceptTouchEvent(true)
+ }
+ downY = ev.y
+ }
+
+ MotionEvent.ACTION_MOVE -> {
+ val y = ev.y.toInt()
+ val yDiff: Float = y - downY
+ if (yDiff < -touchSlop && !canScrollForwardQs()) {
+ // Intercept touches that are overscrolling.
+ return true
+ }
+ }
+ }
+ return super.onInterceptTouchEvent(ev)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt
index 26b2e2b7bd66..424be90ba2ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt
@@ -38,6 +38,6 @@ constructor(
) {
val rows =
configurationRepository.onConfigurationChange.emitOnStart().map {
- resources.getInteger(R.integer.quick_settings_max_rows)
+ resources.getInteger(R.integer.quick_settings_paginated_grid_num_rows)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
index f7c71ceb9e6c..ee0cfb304db0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt
@@ -34,6 +34,6 @@ constructor(
) {
val rows =
configurationRepository.onConfigurationChange.emitOnStart().map {
- resources.getInteger(R.integer.quick_qs_panel_max_rows)
+ resources.getInteger(R.integer.quick_qs_paginated_grid_num_rows)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
new file mode 100644
index 000000000000..b9994d7bb821
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.systemui.qs.panels.ui.compose
+
+import com.android.compose.animation.Bounceable
+import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.ui.model.GridCell
+import com.android.systemui.qs.panels.ui.model.TileGridCell
+import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
+
+data class BounceableInfo(
+ val bounceable: BounceableTileViewModel,
+ val previousTile: Bounceable?,
+ val nextTile: Bounceable?,
+ val bounceEnd: Boolean,
+)
+
+fun List<Pair<GridCell, BounceableTileViewModel>>.bounceableInfo(
+ index: Int,
+ columns: Int,
+): BounceableInfo {
+ val cell = this[index].first as TileGridCell
+ // Only look for neighbor bounceables if they are on the same row
+ val onLastColumn = cell.onLastColumn(cell.column, columns)
+ val previousTile = getOrNull(index - 1)?.takeIf { cell.column != 0 }
+ val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn }
+ return BounceableInfo(this[index].second, previousTile?.second, nextTile?.second, !onLastColumn)
+}
+
+fun List<BounceableTileViewModel>.bounceableInfo(
+ sizedTile: SizedTile<TileViewModel>,
+ index: Int,
+ column: Int,
+ columns: Int,
+): BounceableInfo {
+ // Only look for neighbor bounceables if they are on the same row
+ val onLastColumn = sizedTile.onLastColumn(column, columns)
+ val previousTile = getOrNull(index - 1)?.takeIf { column != 0 }
+ val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn }
+ return BounceableInfo(this[index], previousTile, nextTile, !onLastColumn)
+}
+
+private fun <T> SizedTile<T>.onLastColumn(column: Int, columns: Int): Boolean {
+ return column == columns - width
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
index 770fd785723a..74fa0fef21d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
@@ -116,9 +116,9 @@ class EditTileListState(tiles: List<SizedTile<EditTileViewModel>>, private val c
regenerateGrid()
_tiles.add(insertionIndex.coerceIn(0, _tiles.size), cell)
} else {
- // Add the tile with a temporary row which will get reassigned when
+ // Add the tile with a temporary row/col which will get reassigned when
// regenerating spacers
- _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0))
+ _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0, 0))
}
regenerateGrid()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index e749475479d8..d55763aaeddb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -19,7 +19,7 @@ package com.android.systemui.qs.panels.ui.compose
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons
@@ -97,7 +97,9 @@ constructor(
TileGrid(tiles = page, modifier = Modifier, editModeStart = {})
}
}
- Box(modifier = Modifier.height(FooterHeight).fillMaxWidth()) {
+ // Use requiredHeight so it won't be squished if the view doesn't quite fit. As this is
+ // expected to be inside a scrollable container, this should not be an issue.
+ Box(modifier = Modifier.requiredHeight(FooterHeight).fillMaxWidth()) {
PagerDots(
pagerState = pagerState,
activeColor = MaterialTheme.colorScheme.primary,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index a645b51404e7..1c7a334d3ef2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -20,6 +20,8 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.util.fastMap
@@ -29,6 +31,7 @@ import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
import com.android.systemui.qs.composefragment.ui.GridAnchor
import com.android.systemui.qs.panels.ui.compose.infinitegrid.Tile
+import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
import com.android.systemui.qs.shared.ui.ElementKeys.toElementKey
import com.android.systemui.res.R
@@ -38,10 +41,11 @@ fun SceneScope.QuickQuickSettings(
viewModel: QuickQuickSettingsViewModel,
modifier: Modifier = Modifier,
) {
- val sizedTiles by
- viewModel.tileViewModels.collectAsStateWithLifecycle(initialValue = emptyList())
+ val sizedTiles by viewModel.tileViewModels.collectAsStateWithLifecycle()
val tiles = sizedTiles.fastMap { it.tile }
+ val bounceables = remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle()
+ val scope = rememberCoroutineScope()
DisposableEffect(tiles) {
val token = Any()
@@ -49,6 +53,7 @@ fun SceneScope.QuickQuickSettings(
onDispose { tiles.forEach { it.stopListening(token) } }
}
val columns by viewModel.columns.collectAsStateWithLifecycle()
+ var cellIndex = 0
Box(modifier = modifier) {
GridAnchor()
VerticalSpannedGrid(
@@ -59,11 +64,15 @@ fun SceneScope.QuickQuickSettings(
modifier = Modifier.sysuiResTag("qqs_tile_layout"),
) { spanIndex ->
val it = sizedTiles[spanIndex]
+ val column = cellIndex % columns
+ cellIndex += it.width
Tile(
tile = it.tile,
iconOnly = it.isIcon,
modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
squishiness = { squishiness },
+ coroutineScope = scope,
+ bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index 9ec5a82a18d7..71fa0ac30fb7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -54,6 +55,7 @@ import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.semantics.toggleableState
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.android.compose.modifiers.background
import com.android.compose.modifiers.thenIf
@@ -64,7 +66,6 @@ import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel
import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState
import com.android.systemui.res.R
-import kotlinx.coroutines.delay
private const val TEST_TAG_TOGGLE = "qs_tile_toggle_target"
@@ -138,13 +139,20 @@ fun LargeTileLabels(
accessibilityUiState: AccessibilityUiState? = null,
) {
Column(verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxHeight()) {
- Text(label, color = colors.label, modifier = Modifier.tileMarquee())
+ Text(
+ label,
+ style = MaterialTheme.typography.labelLarge,
+ color = colors.label,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
if (!TextUtils.isEmpty(secondaryLabel)) {
Text(
secondaryLabel ?: "",
color = colors.secondaryLabel,
+ style = MaterialTheme.typography.bodyMedium,
modifier =
- Modifier.tileMarquee().thenIf(
+ Modifier.thenIf(
accessibilityUiState?.stateDescription?.contains(secondaryLabel ?: "") ==
true
) {
@@ -182,10 +190,7 @@ fun SmallTileContent(
rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true)
} else {
var atEnd by remember(icon.res) { mutableStateOf(false) }
- LaunchedEffect(key1 = icon.res) {
- delay(350)
- atEnd = true
- }
+ LaunchedEffect(key1 = icon.res) { atEnd = true }
rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = atEnd)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index 45c1e48840ee..5c2a2bd2b78c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -22,13 +22,13 @@ import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
-import androidx.compose.animation.core.animateIntAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalOverscrollConfiguration
import androidx.compose.foundation.background
import androidx.compose.foundation.border
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
@@ -46,7 +46,6 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.grid.GridCells
-import androidx.compose.foundation.lazy.grid.LazyGridItemScope
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.rememberScrollState
@@ -66,19 +65,17 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.BiasAlignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
-import androidx.compose.ui.graphics.drawscope.Stroke
-import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInRoot
@@ -98,23 +95,26 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastMap
-import androidx.compose.ui.zIndex
+import com.android.compose.animation.bounceable
import com.android.compose.modifiers.background
import com.android.compose.modifiers.height
import com.android.systemui.common.ui.compose.load
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
+import com.android.systemui.qs.panels.ui.compose.BounceableInfo
import com.android.systemui.qs.panels.ui.compose.DragAndDropState
import com.android.systemui.qs.panels.ui.compose.EditTileListState
+import com.android.systemui.qs.panels.ui.compose.bounceableInfo
import com.android.systemui.qs.panels.ui.compose.dragAndDropRemoveZone
import com.android.systemui.qs.panels.ui.compose.dragAndDropTileList
import com.android.systemui.qs.panels.ui.compose.dragAndDropTileSource
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileArrangementPadding
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileHeight
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.ToggleTargetSize
import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding
import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState
-import com.android.systemui.qs.panels.ui.compose.selection.ResizingHandle
+import com.android.systemui.qs.panels.ui.compose.selection.ResizableTileContainer
import com.android.systemui.qs.panels.ui.compose.selection.TileWidths
import com.android.systemui.qs.panels.ui.compose.selection.clearSelectionTile
import com.android.systemui.qs.panels.ui.compose.selection.rememberSelectionState
@@ -122,11 +122,14 @@ import com.android.systemui.qs.panels.ui.compose.selection.selectableTile
import com.android.systemui.qs.panels.ui.model.GridCell
import com.android.systemui.qs.panels.ui.model.SpacerGridCell
import com.android.systemui.qs.panels.ui.model.TileGridCell
+import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.groupAndSort
import com.android.systemui.res.R
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
object TileType
@@ -241,15 +244,20 @@ private fun CurrentTilesGrid(
onSetTiles: (List<TileSpec>) -> Unit,
) {
val currentListState by rememberUpdatedState(listState)
- val tileHeight = CommonTileDefaults.TileHeight
val totalRows = listState.tiles.lastOrNull()?.row ?: 0
val totalHeight by
animateDpAsState(
- gridHeight(totalRows + 1, tileHeight, TileArrangementPadding, CurrentTilesGridPadding),
+ gridHeight(totalRows + 1, TileHeight, TileArrangementPadding, CurrentTilesGridPadding),
label = "QSEditCurrentTilesGridHeight",
)
val gridState = rememberLazyGridState()
var gridContentOffset by remember { mutableStateOf(Offset(0f, 0f)) }
+ val coroutineScope = rememberCoroutineScope()
+
+ val cells =
+ remember(listState.tiles) {
+ listState.tiles.fastMap { Pair(it, BounceableTileViewModel()) }
+ }
TileLazyGrid(
state = gridState,
@@ -272,7 +280,7 @@ private fun CurrentTilesGrid(
}
.testTag(CURRENT_TILES_GRID_TEST_TAG),
) {
- EditTiles(listState.tiles, listState, selectionState) { spec ->
+ EditTiles(cells, columns, listState, selectionState, coroutineScope) { spec ->
// Toggle the current size of the tile
currentListState.isIcon(spec)?.let { onResize(spec, !it) }
}
@@ -286,10 +294,10 @@ private fun AvailableTileGrid(
columns: Int,
dragAndDropState: DragAndDropState,
) {
- // Available tiles aren't visible during drag and drop, so the row isn't needed
+ // Available tiles aren't visible during drag and drop, so the row/col isn't needed
val groupedTiles =
remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) {
- groupAndSort(tiles.fastMap { TileGridCell(it, 0) })
+ groupAndSort(tiles.fastMap { TileGridCell(it, 0, 0) })
}
val labelColors = EditModeTileDefaults.editTileColors()
@@ -349,24 +357,26 @@ private fun GridCell.key(index: Int, dragAndDropState: DragAndDropState): Any {
/**
* Adds a list of [GridCell] to the lazy grid
*
- * @param cells the list of [GridCell]
+ * @param cells the pairs of [GridCell] to [BounceableTileViewModel]
* @param dragAndDropState the [DragAndDropState] for this grid
* @param selectionState the [MutableSelectionState] for this grid
* @param onToggleSize the callback when a tile's size is toggled
*/
fun LazyGridScope.EditTiles(
- cells: List<GridCell>,
+ cells: List<Pair<GridCell, BounceableTileViewModel>>,
+ columns: Int,
dragAndDropState: DragAndDropState,
selectionState: MutableSelectionState,
+ coroutineScope: CoroutineScope,
onToggleSize: (spec: TileSpec) -> Unit,
) {
items(
count = cells.size,
- key = { cells[it].key(it, dragAndDropState) },
- span = { cells[it].span },
+ key = { cells[it].first.key(it, dragAndDropState) },
+ span = { cells[it].first.span },
contentType = { TileType },
) { index ->
- when (val cell = cells[index]) {
+ when (val cell = cells[index].first) {
is TileGridCell ->
if (dragAndDropState.isMoving(cell.tile.tileSpec)) {
// If the tile is being moved, replace it with a visible spacer
@@ -385,6 +395,9 @@ fun LazyGridScope.EditTiles(
dragAndDropState = dragAndDropState,
selectionState = selectionState,
onToggleSize = onToggleSize,
+ coroutineScope = coroutineScope,
+ bounceableInfo = cells.bounceableInfo(index, columns),
+ modifier = Modifier.animateItem(),
)
}
is SpacerGridCell -> SpacerGridCell()
@@ -393,12 +406,15 @@ fun LazyGridScope.EditTiles(
}
@Composable
-private fun LazyGridItemScope.TileGridCell(
+private fun TileGridCell(
cell: TileGridCell,
index: Int,
dragAndDropState: DragAndDropState,
selectionState: MutableSelectionState,
onToggleSize: (spec: TileSpec) -> Unit,
+ coroutineScope: CoroutineScope,
+ bounceableInfo: BounceableInfo,
+ modifier: Modifier = Modifier,
) {
val stateDescription = stringResource(id = R.string.accessibility_qs_edit_position, index + 1)
var selected by remember { mutableStateOf(false) }
@@ -407,6 +423,9 @@ private fun LazyGridItemScope.TileGridCell(
targetValue = if (selected) 1f else 0f,
label = "QSEditTileSelectionAlpha",
)
+ val selectionColor = MaterialTheme.colorScheme.primary
+ val colors = EditModeTileDefaults.editTileColors()
+ val currentBounceableInfo by rememberUpdatedState(bounceableInfo)
LaunchedEffect(selectionState.selection?.tileSpec) {
selectionState.selection?.let {
@@ -420,152 +439,61 @@ private fun LazyGridItemScope.TileGridCell(
selected = selectionState.selection?.tileSpec == cell.tile.tileSpec
}
- val modifier =
- Modifier.animateItem()
- .semantics(mergeDescendants = true) {
- this.stateDescription = stateDescription
- contentDescription = cell.tile.label.text
- customActions =
- listOf(
- // TODO(b/367748260): Add final accessibility actions
- CustomAccessibilityAction("Toggle size") {
- onToggleSize(cell.tile.tileSpec)
- true
- }
- )
- }
- .height(CommonTileDefaults.TileHeight)
- .fillMaxWidth()
-
- val content =
- @Composable {
- EditTile(
- tileViewModel = cell.tile,
- iconOnly = cell.isIcon,
- selectionAlpha = { selectionAlpha },
- modifier =
- Modifier.fillMaxSize()
- .selectableTile(cell.tile.tileSpec, selectionState)
- .dragAndDropTileSource(
- SizedTileImpl(cell.tile, cell.width),
- dragAndDropState,
- selectionState::unSelect,
- ),
- )
- }
-
- if (selected) {
- SelectedTile(
- isIcon = cell.isIcon,
- selectionAlpha = { selectionAlpha },
- selectionState = selectionState,
- modifier = modifier.zIndex(2f), // 2f to display this tile over neighbors when dragged
- content = content,
- )
- } else {
- UnselectedTile(
- selectionAlpha = { selectionAlpha },
- selectionState = selectionState,
- modifier = modifier,
- content = content,
- )
- }
-}
-
-@Composable
-private fun SelectedTile(
- isIcon: Boolean,
- selectionAlpha: () -> Float,
- selectionState: MutableSelectionState,
- modifier: Modifier = Modifier,
- content: @Composable () -> Unit,
-) {
// Current base, min and max width of this tile
var tileWidths: TileWidths? by remember { mutableStateOf(null) }
-
- // Animated diff between the current width and the resized width of the tile. We can't use
- // animateContentSize here as the tile is sometimes unbounded.
- val remainingOffset by
- animateIntAsState(
- selectionState.resizingState?.let { tileWidths?.base?.minus(it.width) ?: 0 } ?: 0,
- label = "QSEditTileWidthOffset",
- )
-
val padding = with(LocalDensity.current) { TileArrangementPadding.roundToPx() }
- Box(
- modifier.onSizeChanged {
- val min = if (isIcon) it.width else (it.width - padding) / 2
- val max = if (isIcon) (it.width * 2) + padding else it.width
- tileWidths = TileWidths(it.width, min, max)
- }
+
+ ResizableTileContainer(
+ selected = selected,
+ selectionState = selectionState,
+ selectionAlpha = { selectionAlpha },
+ selectionColor = selectionColor,
+ tileWidths = { tileWidths },
+ modifier =
+ modifier
+ .height(TileHeight)
+ .fillMaxWidth()
+ .onSizeChanged {
+ // Grab the size before the bounceable to get the idle width
+ val min = if (cell.isIcon) it.width else (it.width - padding) / 2
+ val max = if (cell.isIcon) (it.width * 2) + padding else it.width
+ tileWidths = TileWidths(it.width, min, max)
+ }
+ .bounceable(
+ bounceable = currentBounceableInfo.bounceable,
+ previousBounceable = currentBounceableInfo.previousTile,
+ nextBounceable = currentBounceableInfo.nextTile,
+ orientation = Orientation.Horizontal,
+ bounceEnd = currentBounceableInfo.bounceEnd,
+ ),
) {
- val handle =
- @Composable {
- ResizingHandle(
- enabled = true,
- selectionState = selectionState,
- transition = selectionAlpha,
- tileWidths = { tileWidths },
+ Box(
+ modifier
+ .fillMaxSize()
+ .semantics(mergeDescendants = true) {
+ this.stateDescription = stateDescription
+ contentDescription = cell.tile.label.text
+ customActions =
+ listOf(
+ // TODO(b/367748260): Add final accessibility actions
+ CustomAccessibilityAction("Toggle size") {
+ onToggleSize(cell.tile.tileSpec)
+ true
+ }
+ )
+ }
+ .selectableTile(cell.tile.tileSpec, selectionState) {
+ coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() }
+ }
+ .dragAndDropTileSource(
+ SizedTileImpl(cell.tile, cell.width),
+ dragAndDropState,
+ selectionState::unSelect,
)
- }
-
- Layout(contents = listOf(content, handle)) {
- (contentMeasurables, handleMeasurables),
- constraints ->
- // Grab the width from the resizing state if a resize is in progress, otherwise fill the
- // max width
- val width =
- selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset)
- val contentPlaceable =
- contentMeasurables.first().measure(constraints.copy(maxWidth = width))
- val handlePlaceable = handleMeasurables.first().measure(constraints)
-
- // Place the dot vertically centered on the right edge
- val handleX = contentPlaceable.width - (handlePlaceable.width / 2)
- val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2)
-
- layout(constraints.maxWidth, constraints.maxHeight) {
- contentPlaceable.place(0, 0)
- handlePlaceable.place(handleX, handleY)
- }
- }
- }
-}
-
-@Composable
-private fun UnselectedTile(
- selectionAlpha: () -> Float,
- selectionState: MutableSelectionState,
- modifier: Modifier = Modifier,
- content: @Composable () -> Unit,
-) {
- val handle =
- @Composable {
- ResizingHandle(
- enabled = false,
- selectionState = selectionState,
- transition = selectionAlpha,
- )
- }
-
- Box(modifier) {
- Layout(contents = listOf(content, handle)) {
- (contentMeasurables, handleMeasurables),
- constraints ->
- val contentPlaceable =
- contentMeasurables
- .first()
- .measure(constraints.copy(maxWidth = constraints.maxWidth))
- val handlePlaceable = handleMeasurables.first().measure(constraints)
-
- // Place the dot vertically centered on the right edge
- val handleX = contentPlaceable.width - (handlePlaceable.width / 2)
- val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2)
-
- layout(constraints.maxWidth, constraints.maxHeight) {
- contentPlaceable.place(0, 0)
- handlePlaceable.place(handleX, handleY)
- }
+ .tileBackground(colors.background)
+ .tilePadding()
+ ) {
+ EditTile(tile = cell.tile, iconOnly = cell.isIcon)
}
}
}
@@ -588,19 +516,19 @@ private fun AvailableTileGridCell(
verticalArrangement = spacedBy(CommonTileDefaults.TilePadding, Alignment.Top),
modifier = modifier,
) {
- EditTileContainer(
- colors = colors,
- modifier =
- Modifier.fillMaxWidth()
- .height(CommonTileDefaults.TileHeight)
- .clearSelectionTile(selectionState)
- .semantics(mergeDescendants = true) {
- onClick(onClickActionName) { false }
- this.stateDescription = stateDescription
- }
- .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) {
- selectionState.unSelect()
- },
+ Box(
+ Modifier.fillMaxWidth()
+ .height(TileHeight)
+ .clearSelectionTile(selectionState)
+ .semantics(mergeDescendants = true) {
+ onClick(onClickActionName) { false }
+ this.stateDescription = stateDescription
+ }
+ .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) {
+ selectionState.unSelect()
+ }
+ .tileBackground(colors.background)
+ .tilePadding()
) {
// Icon
SmallTileContent(
@@ -626,16 +554,14 @@ private fun AvailableTileGridCell(
@Composable
private fun SpacerGridCell(modifier: Modifier = Modifier) {
// By default, spacers are invisible and exist purely to catch drag movements
- Box(modifier.height(CommonTileDefaults.TileHeight).fillMaxWidth())
+ Box(modifier.height(TileHeight).fillMaxWidth())
}
@Composable
-fun EditTile(
- tileViewModel: EditTileViewModel,
+fun BoxScope.EditTile(
+ tile: EditTileViewModel,
iconOnly: Boolean,
- modifier: Modifier = Modifier,
colors: TileColors = EditModeTileDefaults.editTileColors(),
- selectionAlpha: () -> Float = { 1f },
) {
// Animated horizontal alignment from center (0f) to start (-1f)
val alignmentValue by
@@ -646,68 +572,36 @@ fun EditTile(
val alignment by remember {
derivedStateOf { BiasAlignment(horizontalBias = alignmentValue, verticalBias = 0f) }
}
+ // Icon
+ Box(Modifier.size(ToggleTargetSize).align(alignment)) {
+ SmallTileContent(
+ icon = tile.icon,
+ color = colors.icon,
+ animateToEnd = true,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ }
- EditTileContainer(colors = colors, selectionAlpha = selectionAlpha, modifier = modifier) {
- // Icon
- Box(Modifier.size(ToggleTargetSize).align(alignment)) {
- SmallTileContent(
- icon = tileViewModel.icon,
- color = colors.icon,
- animateToEnd = true,
- modifier = Modifier.align(Alignment.Center),
- )
- }
-
- // Labels, positioned after the icon
- AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) {
- LargeTileLabels(
- label = tileViewModel.label.text,
- secondaryLabel = tileViewModel.appName?.text,
- colors = colors,
- modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding),
- )
- }
+ // Labels, positioned after the icon
+ AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) {
+ LargeTileLabels(
+ label = tile.label.text,
+ secondaryLabel = tile.appName?.text,
+ colors = colors,
+ modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding),
+ )
}
}
-@Composable
-private fun EditTileContainer(
- colors: TileColors,
- modifier: Modifier = Modifier,
- selectionAlpha: () -> Float = { 0f },
- selectionColor: Color = MaterialTheme.colorScheme.primary,
- content: @Composable BoxScope.() -> Unit = {},
-) {
- Box(
- Modifier.wrapContentSize().drawWithContent {
- drawContent()
- drawRoundRect(
- SolidColor(selectionColor),
- cornerRadius = CornerRadius(InactiveCornerRadius.toPx()),
- style = Stroke(EditModeTileDefaults.SelectedBorderWidth.toPx()),
- alpha = selectionAlpha(),
- )
- }
- ) {
- Box(
- modifier =
- modifier
- .drawBehind {
- drawRoundRect(
- SolidColor(colors.background),
- cornerRadius = CornerRadius(InactiveCornerRadius.toPx()),
- )
- }
- .tilePadding(),
- content = content,
- )
+private fun Modifier.tileBackground(color: Color): Modifier {
+ return drawBehind {
+ drawRoundRect(SolidColor(color), cornerRadius = CornerRadius(InactiveCornerRadius.toPx()))
}
}
private object EditModeTileDefaults {
const val PLACEHOLDER_ALPHA = .3f
val EditGridHeaderHeight = 60.dp
- val SelectedBorderWidth = 2.dp
val CurrentTilesGridPadding = 8.dp
@Composable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index 6920e498bdde..e5c213519415 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.util.fastMap
@@ -29,7 +30,9 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout
+import com.android.systemui.qs.panels.ui.compose.bounceableInfo
import com.android.systemui.qs.panels.ui.compose.rememberEditListState
+import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsViewModel
@@ -62,7 +65,11 @@ constructor(
}
val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) }
+ val bounceables =
+ remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
val squishiness by squishinessViewModel.squishiness.collectAsStateWithLifecycle()
+ val scope = rememberCoroutineScope()
+ var cellIndex = 0
VerticalSpannedGrid(
columns = columns,
@@ -71,11 +78,15 @@ constructor(
spans = sizedTiles.fastMap { it.width },
) { spanIndex ->
val it = sizedTiles[spanIndex]
+ val column = cellIndex % columns
+ cellIndex += it.width
Tile(
tile = it.tile,
iconOnly = iconTilesViewModel.isIconTile(it.tile.spec),
modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)),
squishiness = { squishiness },
+ coroutineScope = scope,
+ bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index 4bd5b2d68c4c..52d526123430 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -23,8 +23,8 @@ import android.service.quicksettings.Tile.STATE_ACTIVE
import android.service.quicksettings.Tile.STATE_INACTIVE
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
@@ -44,6 +44,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -61,11 +62,13 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Expandable
+import com.android.compose.animation.bounceable
import com.android.compose.modifiers.thenIf
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.panels.ui.compose.BounceableInfo
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel
import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
@@ -74,6 +77,8 @@ import com.android.systemui.qs.panels.ui.viewmodel.toUiState
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.res.R
import java.util.function.Supplier
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
private const val TEST_TAG_SMALL = "qs_tile_small"
private const val TEST_TAG_LARGE = "qs_tile_large"
@@ -102,9 +107,12 @@ fun Tile(
tile: TileViewModel,
iconOnly: Boolean,
squishiness: () -> Float,
+ coroutineScope: CoroutineScope,
+ bounceableInfo: BounceableInfo,
modifier: Modifier = Modifier,
) {
val state by tile.state.collectAsStateWithLifecycle(tile.currentState)
+ val currentBounceableInfo by rememberUpdatedState(bounceableInfo)
val resources = resources()
val uiState = remember(state, resources) { state.toUiState(resources) }
val colors = TileDefaults.getColorForState(uiState)
@@ -112,7 +120,7 @@ fun Tile(
// TODO(b/361789146): Draw the shapes instead of clipping
val tileShape = TileDefaults.animateTileShape(uiState.state)
- TileContainer(
+ TileExpandable(
color =
if (iconOnly || !uiState.handlesSecondaryClick) {
colors.iconBackground
@@ -120,93 +128,101 @@ fun Tile(
colors.background
},
shape = tileShape,
- iconOnly = iconOnly,
- onClick = tile::onClick,
- onLongClick = tile::onLongClick,
- uiState = uiState,
squishiness = squishiness,
- modifier = modifier,
+ modifier =
+ modifier
+ .fillMaxWidth()
+ .bounceable(
+ bounceable = currentBounceableInfo.bounceable,
+ previousBounceable = currentBounceableInfo.previousTile,
+ nextBounceable = currentBounceableInfo.nextTile,
+ orientation = Orientation.Horizontal,
+ bounceEnd = currentBounceableInfo.bounceEnd,
+ ),
) { expandable ->
- val icon = getTileIcon(icon = uiState.icon)
- if (iconOnly) {
- SmallTileContent(
- icon = icon,
- color = colors.icon,
- modifier = Modifier.align(Alignment.Center),
- )
- } else {
- val iconShape = TileDefaults.animateIconShape(uiState.state)
- LargeTileContent(
- label = uiState.label,
- secondaryLabel = uiState.secondaryLabel,
- icon = icon,
- colors = colors,
- iconShape = iconShape,
- toggleClickSupported = state.handlesSecondaryClick,
- onClick = {
- if (state.handlesSecondaryClick) {
- tile.onSecondaryClick()
- }
- },
- onLongClick = { tile.onLongClick(expandable) },
- accessibilityUiState = uiState.accessibilityUiState,
- squishiness = squishiness,
- )
+ TileContainer(
+ onClick = {
+ tile.onClick(expandable)
+ if (uiState.accessibilityUiState.toggleableState != null) {
+ coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() }
+ }
+ },
+ onLongClick = { tile.onLongClick(expandable) },
+ uiState = uiState,
+ iconOnly = iconOnly,
+ ) {
+ val icon = getTileIcon(icon = uiState.icon)
+ if (iconOnly) {
+ SmallTileContent(
+ icon = icon,
+ color = colors.icon,
+ modifier = Modifier.align(Alignment.Center),
+ )
+ } else {
+ val iconShape = TileDefaults.animateIconShape(uiState.state)
+ LargeTileContent(
+ label = uiState.label,
+ secondaryLabel = uiState.secondaryLabel,
+ icon = icon,
+ colors = colors,
+ iconShape = iconShape,
+ toggleClickSupported = state.handlesSecondaryClick,
+ onClick = {
+ if (state.handlesSecondaryClick) {
+ tile.onSecondaryClick()
+ }
+ },
+ onLongClick = { tile.onLongClick(expandable) },
+ accessibilityUiState = uiState.accessibilityUiState,
+ squishiness = squishiness,
+ )
+ }
}
}
}
@Composable
-private fun TileContainer(
+private fun TileExpandable(
color: Color,
shape: Shape,
- iconOnly: Boolean,
- uiState: TileUiState,
squishiness: () -> Float,
modifier: Modifier = Modifier,
- onClick: (Expandable) -> Unit = {},
- onLongClick: (Expandable) -> Unit = {},
- content: @Composable BoxScope.(Expandable) -> Unit,
+ content: @Composable (Expandable) -> Unit,
) {
Expandable(
color = color,
shape = shape,
modifier = modifier.clip(shape).verticalSquish(squishiness),
) {
- val longPressLabel = longPressLabel()
- Box(
- modifier =
- Modifier.height(CommonTileDefaults.TileHeight)
- .fillMaxWidth()
- .combinedClickable(
- onClick = { onClick(it) },
- onLongClick = { onLongClick(it) },
- onClickLabel = uiState.accessibilityUiState.clickLabel,
- onLongClickLabel = longPressLabel,
- )
- .semantics {
- role = uiState.accessibilityUiState.accessibilityRole
- if (uiState.accessibilityUiState.accessibilityRole == Role.Switch) {
- uiState.accessibilityUiState.toggleableState?.let {
- toggleableState = it
- }
- }
- stateDescription = uiState.accessibilityUiState.stateDescription
- }
- .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE)
- .thenIf(iconOnly) {
- Modifier.semantics {
- contentDescription = uiState.accessibilityUiState.contentDescription
- }
- }
- .tilePadding()
- ) {
- content(it)
- }
+ content(it)
}
}
@Composable
+fun TileContainer(
+ onClick: () -> Unit,
+ onLongClick: () -> Unit,
+ uiState: TileUiState,
+ iconOnly: Boolean,
+ content: @Composable BoxScope.() -> Unit,
+) {
+ Box(
+ modifier =
+ Modifier.height(CommonTileDefaults.TileHeight)
+ .fillMaxWidth()
+ .tileCombinedClickable(
+ onClick = onClick,
+ onLongClick = onLongClick,
+ uiState = uiState,
+ iconOnly = iconOnly,
+ )
+ .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE)
+ .tilePadding(),
+ content = content,
+ )
+}
+
+@Composable
private fun getTileIcon(icon: Supplier<QSTile.Icon?>): Icon {
val context = LocalContext.current
return icon.get()?.let {
@@ -222,14 +238,38 @@ fun tileHorizontalArrangement(): Arrangement.Horizontal {
return spacedBy(space = CommonTileDefaults.TileArrangementPadding, alignment = Alignment.Start)
}
-fun Modifier.tileMarquee(): Modifier {
- return basicMarquee(iterations = 1, initialDelayMillis = 200)
-}
-
fun Modifier.tilePadding(): Modifier {
return padding(CommonTileDefaults.TilePadding)
}
+@Composable
+fun Modifier.tileCombinedClickable(
+ onClick: () -> Unit,
+ onLongClick: () -> Unit,
+ uiState: TileUiState,
+ iconOnly: Boolean,
+): Modifier {
+ val longPressLabel = longPressLabel()
+ return combinedClickable(
+ onClick = onClick,
+ onLongClick = onLongClick,
+ onClickLabel = uiState.accessibilityUiState.clickLabel,
+ onLongClickLabel = longPressLabel,
+ )
+ .semantics {
+ role = uiState.accessibilityUiState.accessibilityRole
+ if (uiState.accessibilityUiState.accessibilityRole == Role.Switch) {
+ uiState.accessibilityUiState.toggleableState?.let { toggleableState = it }
+ }
+ stateDescription = uiState.accessibilityUiState.stateDescription
+ }
+ .thenIf(iconOnly) {
+ Modifier.semantics {
+ contentDescription = uiState.accessibilityUiState.contentDescription
+ }
+ }
+}
+
data class TileColors(
val background: Color,
val iconBackground: Color,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
index 441d96289d86..1d36aee4eb85 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
@@ -89,8 +89,11 @@ class MutableSelectionState(
* Listens for click events to select/unselect the given [TileSpec]. Use this on current tiles as
* they can be selected.
*/
-@Composable
-fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelectionState): Modifier {
+fun Modifier.selectableTile(
+ tileSpec: TileSpec,
+ selectionState: MutableSelectionState,
+ onClick: () -> Unit = {},
+): Modifier {
return pointerInput(Unit) {
detectTapGestures(
onTap = {
@@ -99,6 +102,7 @@ fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelection
} else {
selectionState.select(tileSpec, manual = true)
}
+ onClick()
}
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
index e0f0b6aa8919..9f13a3788f53 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
@@ -16,63 +16,117 @@
package com.android.systemui.qs.panels.ui.compose.selection
+import androidx.compose.animation.core.animateIntAsState
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.systemGestureExclusion
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.zIndex
+import com.android.compose.modifiers.thenIf
+import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius
import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingDotSize
+import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.SelectedBorderWidth
/**
- * Dot handling resizing drag events. Use this on the selected tile to resize it
+ * Places a dot to handle resizing drag events. Use this on tiles to resize.
*
- * @param enabled whether resizing drag events should be handled
+ * The dot is placed vertically centered on the right border. The [content] will have a border when
+ * selected.
+ *
+ * @param selected whether resizing drag events should be handled
* @param selectionState the [MutableSelectionState] on the grid
- * @param transition the animated value for the dot, used for its alpha and scale
+ * @param selectionAlpha the animated value for the dot and border alpha
+ * @param selectionColor the [Color] of the dot and border
* @param tileWidths the [TileWidths] of the selected tile
- * @param onResize the callback when the drag passes the resizing threshold
*/
@Composable
-fun ResizingHandle(
+fun ResizableTileContainer(
+ selected: Boolean,
+ selectionState: MutableSelectionState,
+ selectionAlpha: () -> Float,
+ selectionColor: Color,
+ tileWidths: () -> TileWidths?,
+ modifier: Modifier = Modifier,
+ content: @Composable BoxScope.() -> Unit = {},
+) {
+ Box(
+ modifier
+ .resizable(selected, selectionState, tileWidths)
+ .selectionBorder(selectionColor, selectionAlpha)
+ ) {
+ content()
+ ResizingHandle(
+ enabled = selected,
+ selectionState = selectionState,
+ transition = selectionAlpha,
+ tileWidths = tileWidths,
+ modifier =
+ // Higher zIndex to make sure the handle is drawn above the content
+ Modifier.zIndex(2f),
+ )
+ }
+}
+
+@Composable
+private fun ResizingHandle(
enabled: Boolean,
selectionState: MutableSelectionState,
transition: () -> Float,
- tileWidths: () -> TileWidths? = { null },
+ tileWidths: () -> TileWidths?,
+ modifier: Modifier = Modifier,
) {
- if (enabled) {
- // Manually creating the touch target around the resizing dot to ensure that the next tile
- // does
- // not receive the touch input accidentally.
- val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current
- Box(
- Modifier.size(minTouchTargetSize)
- .systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) }
- .pointerInput(Unit) {
- detectHorizontalDragGestures(
- onHorizontalDrag = { _, offset -> selectionState.onResizingDrag(offset) },
- onDragStart = {
- tileWidths()?.let { selectionState.onResizingDragStart(it) }
- },
- onDragEnd = selectionState::onResizingDragEnd,
- onDragCancel = selectionState::onResizingDragEnd,
+ // Manually creating the touch target around the resizing dot to ensure that the next tile
+ // does not receive the touch input accidentally.
+ val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current
+ Box(
+ modifier
+ .layout { measurable, constraints ->
+ val size = minTouchTargetSize.roundToPx()
+ val placeable = measurable.measure(Constraints(size, size, size, size))
+ layout(placeable.width, placeable.height) {
+ placeable.place(
+ x = constraints.maxWidth - placeable.width / 2,
+ y = constraints.maxHeight / 2 - placeable.height / 2,
)
}
- ) {
- ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center))
- }
- } else {
- ResizingDot(transition = transition)
+ }
+ .thenIf(enabled) {
+ Modifier.systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) }
+ .pointerInput(Unit) {
+ detectHorizontalDragGestures(
+ onHorizontalDrag = { _, offset ->
+ selectionState.onResizingDrag(offset)
+ },
+ onDragStart = {
+ tileWidths()?.let { selectionState.onResizingDragStart(it) }
+ },
+ onDragEnd = selectionState::onResizingDragEnd,
+ onDragCancel = selectionState::onResizingDragEnd,
+ )
+ }
+ }
+ ) {
+ ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center))
}
}
@@ -88,6 +142,45 @@ private fun ResizingDot(
}
}
+private fun Modifier.selectionBorder(
+ selectionColor: Color,
+ selectionAlpha: () -> Float = { 0f },
+): Modifier {
+ return drawWithContent {
+ drawContent()
+ drawRoundRect(
+ SolidColor(selectionColor),
+ cornerRadius = CornerRadius(InactiveCornerRadius.toPx()),
+ style = Stroke(SelectedBorderWidth.toPx()),
+ alpha = selectionAlpha(),
+ )
+ }
+}
+
+@Composable
+private fun Modifier.resizable(
+ selected: Boolean,
+ selectionState: MutableSelectionState,
+ tileWidths: () -> TileWidths?,
+): Modifier {
+ if (!selected) return zIndex(1f)
+
+ // Animated diff between the current width and the resized width of the tile. We can't use
+ // animateContentSize here as the tile is sometimes unbounded.
+ val remainingOffset by
+ animateIntAsState(
+ selectionState.resizingState?.let { tileWidths()?.base?.minus(it.width) ?: 0 } ?: 0,
+ label = "QSEditTileWidthOffset",
+ )
+ return zIndex(2f).layout { measurable, constraints ->
+ // Grab the width from the resizing state if a resize is in progress
+ val width = selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset)
+ val placeable = measurable.measure(constraints.copy(minWidth = width, maxWidth = width))
+ layout(constraints.maxWidth, placeable.height) { placeable.place(0, 0) }
+ }
+}
+
private object SelectionDefaults {
val ResizingDotSize = 16.dp
+ val SelectedBorderWidth = 2.dp
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
index b1841c4c5ffa..c0441f8a38a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt
@@ -31,8 +31,8 @@ sealed interface GridCell {
}
/**
- * Represents a [EditTileViewModel] from a grid associated with a tile format and the row it's
- * positioned at
+ * Represents a [EditTileViewModel] from a grid associated with a tile format and the row and column
+ * it's positioned at
*/
@Immutable
data class TileGridCell(
@@ -41,13 +41,15 @@ data class TileGridCell(
override val width: Int,
override val span: GridItemSpan = GridItemSpan(width),
override val s: String = "${tile.tileSpec.spec}-$row-$width",
+ val column: Int,
) : GridCell, SizedTile<EditTileViewModel>, CategoryAndName by tile {
val key: String = "${tile.tileSpec.spec}-$row"
constructor(
sizedTile: SizedTile<EditTileViewModel>,
row: Int,
- ) : this(tile = sizedTile.tile, row = row, width = sizedTile.width)
+ column: Int,
+ ) : this(tile = sizedTile.tile, row = row, column = column, width = sizedTile.width)
}
/** Represents an empty space used to fill incomplete rows. Will always display as a 1x1 tile */
@@ -73,7 +75,13 @@ fun List<SizedTile<EditTileViewModel>>.toGridCells(
return splitInRowsSequence(this, columns)
.flatMapIndexed { rowIndex, sizedTiles ->
val correctedRowIndex = rowIndex + startingRow
- val row: List<GridCell> = sizedTiles.map { TileGridCell(it, correctedRowIndex) }
+ var column = 0
+ val row: List<GridCell> =
+ sizedTiles.map {
+ TileGridCell(it, correctedRowIndex, column).also { cell ->
+ column += cell.width
+ }
+ }
// Fill the incomplete rows with spacers
val numSpacers = columns - sizedTiles.sumOf { it.width }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt
new file mode 100644
index 000000000000..506b05256880
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.systemui.qs.panels.ui.viewmodel
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.VectorConverter
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.Bounceable
+
+class BounceableTileViewModel : Bounceable {
+ private val animatableBounce = Animatable(0.dp, Dp.VectorConverter)
+ override val bounce: Dp
+ get() = animatableBounce.value
+
+ suspend fun animateBounce() {
+ animatableBounce.animateTo(BounceSize)
+ animatableBounce.animateTo(0.dp)
+ }
+
+ private companion object {
+ val BounceSize = 8.dp
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
index ee12736f6db4..be6ce5c5b4f4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
@@ -43,13 +43,13 @@ data class UnloadedEditTileViewModel(
) {
fun load(context: Context): EditTileViewModel {
return EditTileViewModel(
- tileSpec,
- icon,
- label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec),
- appName?.toAnnotatedString(context),
- isCurrent,
- availableEditActions,
- category,
+ tileSpec = tileSpec,
+ icon = icon,
+ label = label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec),
+ appName = appName?.toAnnotatedString(context),
+ isCurrent = isCurrent,
+ availableEditActions = availableEditActions,
+ category = category,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
index b8f4ab40bb1d..dde36289f139 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
@@ -16,7 +16,7 @@
package com.android.systemui.qs.tiles.base.viewmodel
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.qualifiers.Application
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -28,5 +28,7 @@ class QSTileCoroutineScopeFactory
constructor(@Application private val applicationScope: CoroutineScope) {
fun create(): CoroutineScope =
- CoroutineScope(applicationScope.coroutineContext + SupervisorJob() + createCoroutineTracingContext("QSTileScope"))
+ CoroutineScope(
+ applicationScope.coroutineContext + SupervisorJob() + newTracingContext("QSTileScope")
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
index ae56c2aad4e9..f6749715e1fd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
@@ -15,12 +15,12 @@
*/
package com.android.systemui.qs.tiles.dialog
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
import android.util.Log
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -63,7 +63,7 @@ constructor(
}
return
} else {
- coroutineScope = CoroutineScope(bgDispatcher + createCoroutineTracingContext("InternetDialogScope"))
+ coroutineScope = CoroutineScope(bgDispatcher + newTracingContext("InternetDialogScope"))
dialog =
dialogFactory
.create(aboveStatusBar, canConfigMobileData, canConfigWifi, coroutineScope)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
new file mode 100644
index 000000000000..8dd611f9911a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.systemui.qs.tiles.impl.hearingdevices.domain
+
+import android.content.res.Resources
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Maps [HearingDevicesTileModel] to [QSTileState]. */
+class HearingDevicesTileMapper
+@Inject
+constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
+ QSTileDataToStateMapper<HearingDevicesTileModel> {
+
+ override fun map(config: QSTileConfig, data: HearingDevicesTileModel): QSTileState =
+ QSTileState.build(resources, theme, config.uiConfig) {
+ label = resources.getString(R.string.quick_settings_hearing_devices_label)
+ iconRes = R.drawable.qs_hearing_devices_icon
+ val loadedIcon =
+ Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+ icon = { loadedIcon }
+ sideViewIcon = QSTileState.SideViewIcon.Chevron
+ contentDescription = label
+ if (data.isAnyActiveHearingDevice) {
+ activationState = QSTileState.ActivationState.ACTIVE
+ secondaryLabel =
+ resources.getString(R.string.quick_settings_hearing_devices_connected)
+ } else if (data.isAnyPairedHearingDevice) {
+ activationState = QSTileState.ActivationState.INACTIVE
+ secondaryLabel =
+ resources.getString(R.string.quick_settings_hearing_devices_disconnected)
+ } else {
+ activationState = QSTileState.ActivationState.INACTIVE
+ secondaryLabel = ""
+ }
+ supportedActions =
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
new file mode 100644
index 000000000000..ec0a4e9db896
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.systemui.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.Flags
+import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.statusbar.policy.BluetoothController
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+
+/** Observes hearing devices state changes providing the [HearingDevicesTileModel]. */
+class HearingDevicesTileDataInteractor
+@Inject
+constructor(
+ @Background private val backgroundContext: CoroutineContext,
+ private val bluetoothController: BluetoothController,
+ private val hearingDevicesChecker: HearingDevicesChecker,
+) : QSTileDataInteractor<HearingDevicesTileModel> {
+ override fun tileData(
+ user: UserHandle,
+ triggers: Flow<DataUpdateTrigger>,
+ ): Flow<HearingDevicesTileModel> =
+ conflatedCallbackFlow {
+ val callback =
+ object : BluetoothController.Callback {
+ override fun onBluetoothStateChange(enabled: Boolean) {
+ trySend(getModel())
+ }
+
+ override fun onBluetoothDevicesChanged() {
+ trySend(getModel())
+ }
+ }
+ bluetoothController.addCallback(callback)
+ awaitClose { bluetoothController.removeCallback(callback) }
+ }
+ .flowOn(backgroundContext)
+ .distinctUntilChanged()
+
+ override fun availability(user: UserHandle): Flow<Boolean> =
+ flowOf(Flags.hearingAidsQsTileDialog())
+
+ private fun getModel() =
+ HearingDevicesTileModel(
+ hearingDevicesChecker.isAnyActiveHearingDevice,
+ hearingDevicesChecker.isAnyPairedHearingDevice,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
new file mode 100644
index 000000000000..5e7172ee3ba7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.systemui.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
+import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
+
+/** Handles hearing devices tile clicks. */
+class HearingDevicesTileUserActionInteractor
+@Inject
+constructor(
+ @Main private val mainContext: CoroutineContext,
+ private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+ private val hearingDevicesDialogManager: HearingDevicesDialogManager,
+) : QSTileUserActionInteractor<HearingDevicesTileModel> {
+
+ override suspend fun handleInput(input: QSTileInput<HearingDevicesTileModel>) =
+ with(input) {
+ when (action) {
+ is QSTileUserAction.Click -> {
+ withContext(mainContext) {
+ hearingDevicesDialogManager.showDialog(
+ action.expandable,
+ LAUNCH_SOURCE_QS_TILE,
+ )
+ }
+ }
+ is QSTileUserAction.LongClick -> {
+ qsTileIntentUserActionHandler.handle(
+ action.expandable,
+ Intent(Settings.ACTION_HEARING_DEVICES_SETTINGS),
+ )
+ }
+ is QSTileUserAction.ToggleClick -> {}
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt
new file mode 100644
index 000000000000..4e37b771c49b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.systemui.qs.tiles.impl.hearingdevices.domain.model
+
+/** Hearing devices tile model */
+data class HearingDevicesTileModel(
+ val isAnyActiveHearingDevice: Boolean,
+ val isAnyPairedHearingDevice: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
index ac75932b8fee..14115444fe49 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
@@ -16,9 +16,9 @@
package com.android.systemui.qs.tiles.impl.location.domain.interactor
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
import android.content.Intent
import android.provider.Settings
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.ActivityStarter
@@ -53,9 +53,11 @@ constructor(
val wasEnabled: Boolean = input.data.isEnabled
if (keyguardController.isMethodSecure() && keyguardController.isShowing()) {
activityStarter.postQSRunnableDismissingKeyguard {
- CoroutineScope(applicationScope.coroutineContext + createCoroutineTracingContext("LocationTileScope")).launch {
- locationController.setLocationEnabled(!wasEnabled)
- }
+ CoroutineScope(
+ applicationScope.coroutineContext +
+ newTracingContext("LocationTileScope")
+ )
+ .launch { locationController.setLocationEnabled(!wasEnabled) }
}
} else {
withContext(coroutineContext) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
index 40591bf56e0a..cc14e71986f5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
@@ -18,7 +18,7 @@ package com.android.systemui.qs.tiles.impl.modes.domain.interactor
import android.content.Context
import android.os.UserHandle
-import com.android.app.tracing.coroutines.flow.map
+import com.android.app.tracing.coroutines.flow.flowName
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.dagger.qualifiers.Background
@@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
class ModesTileDataInteractor
@Inject
@@ -59,6 +60,7 @@ constructor(
fun tileData() =
zenModeInteractor.activeModes
.map { activeModes -> buildTileData(activeModes) }
+ .flowName("tileData")
.flowOn(bgDispatcher)
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
index 801a0ce4b744..537b56bccae8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
@@ -39,7 +39,7 @@ constructor(@Main private val resources: Resources, val theme: Resources.Theme)
val loadedIcon: Icon.Loaded =
when (val dataIcon = data.icon) {
is Icon.Resource -> {
- if (iconRes != dataIcon.res) {
+ if (data.iconResId != dataIcon.res) {
Log.wtf(
"ModesTileMapper",
"Icon.Resource.res & iconResId are not identical",
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
index 468e180a6e41..f03c7521931c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt
@@ -16,12 +16,12 @@
package com.android.systemui.qs.tiles.impl.saver.domain
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
import android.content.Context
import android.content.DialogInterface
import android.content.SharedPreferences
import android.os.Bundle
import com.android.internal.R
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileUserActionInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.DataSaverController
@@ -45,9 +45,8 @@ class DataSaverDialogDelegate(
setTitle(R.string.data_saver_enable_title)
setMessage(R.string.data_saver_description)
setPositiveButton(R.string.data_saver_enable_button) { _: DialogInterface?, _ ->
- CoroutineScope(backgroundContext + createCoroutineTracingContext("DataSaverDialogScope")).launch {
- dataSaverController.setDataSaverEnabled(true)
- }
+ CoroutineScope(backgroundContext + newTracingContext("DataSaverDialogScope"))
+ .launch { dataSaverController.setDataSaverEnabled(true) }
sharedPreferences
.edit()
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
index 7b6b0f614cc2..6097ef53f8df 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
@@ -20,7 +20,6 @@ package com.android.systemui.scene.shared.flag
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.Flags.sceneContainer
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
@@ -42,8 +41,7 @@ object SceneContainerFlag {
KeyguardWmStateRefactor.isEnabled &&
MigrateClocksToBlueprint.isEnabled &&
NotificationThrottleHun.isEnabled &&
- PredictiveBackSysUiFlag.isEnabled &&
- DeviceEntryUdfpsRefactor.isEnabled
+ PredictiveBackSysUiFlag.isEnabled
// NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
@@ -58,7 +56,6 @@ object SceneContainerFlag {
MigrateClocksToBlueprint.token,
NotificationThrottleHun.token,
PredictiveBackSysUiFlag.token,
- DeviceEntryUdfpsRefactor.token,
// NOTE: Changes should also be made in isEnabled and @EnableSceneContainer
)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index ce942fefa393..47254775618c 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -19,7 +19,7 @@ package com.android.systemui.scene.ui.viewmodel
import android.view.MotionEvent
import android.view.View
import androidx.compose.runtime.getValue
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ContentKey
import com.android.compose.animation.scene.DefaultEdgeDetector
import com.android.compose.animation.scene.ObservableTransitionState
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
index 54e0319da58f..17b1b6d91229 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
@@ -26,7 +26,7 @@ import android.os.UserHandle
import android.util.Log
import android.util.Pair
import android.view.Window
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.app.ChooserActivity
import com.android.systemui.dagger.qualifiers.Application
import dagger.assisted.Assisted
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 9e622801a01b..7b01c36489fb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -31,7 +31,7 @@ import android.view.RemoteAnimationAdapter
import android.view.RemoteAnimationTarget
import android.view.WindowManager
import android.view.WindowManagerGlobal
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.infra.ServiceConnector
import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
index acfcd13738f0..2259b55dc268 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
@@ -313,7 +313,9 @@ public class LegacyScreenshotController implements InteractiveScreenshotHandler
setWindowFocusable(true);
mViewProxy.requestFocus();
- enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle());
+ if (screenshot.getType() != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+ enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle());
+ }
attachWindow();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
index f5c605211520..08214c456897 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
@@ -35,6 +35,7 @@ import android.util.Log
import android.view.Display
import android.view.ScrollCaptureResponse
import android.view.ViewRootImpl.ActivityConfigCallback
+import android.view.WindowManager
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import android.widget.Toast
import android.window.WindowContext
@@ -217,7 +218,9 @@ internal constructor(
window.setFocusable(true)
viewProxy.requestFocus()
- enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
+ if (screenshot.type != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+ enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
+ }
window.attachWindow()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
index c8067df114fc..6df22f036273 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
@@ -21,7 +21,7 @@ import android.os.RemoteException
import android.util.Log
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.shade.ShadeExpansionStateManager
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
index d3a7fc4a3e4a..7aeec47241cb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
@@ -18,7 +18,7 @@ package com.android.systemui.screenshot
import android.media.MediaPlayer
import android.util.Log
-import com.android.app.tracing.coroutines.async
+import com.android.app.tracing.coroutines.asyncTraced as async
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 757e37e9341f..d652d5bec27b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -123,7 +123,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
@@ -1859,16 +1858,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
/** Returns space between top of lock icon and bottom of NotificationStackScrollLayout. */
private float getLockIconPadding() {
float lockIconPadding = 0f;
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- View deviceEntryIconView = mKeyguardViewConfigurator.getKeyguardRootView()
- .findViewById(R.id.device_entry_icon_view);
- if (deviceEntryIconView != null) {
- lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
- - deviceEntryIconView.getTop();
- }
- } else if (mLockIconViewController.getTop() != 0f) {
+ View deviceEntryIconView = mKeyguardViewConfigurator.getKeyguardRootView()
+ .findViewById(R.id.device_entry_icon_view);
+ if (deviceEntryIconView != null) {
lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
- - mLockIconViewController.getTop();
+ - deviceEntryIconView.getTop();
}
return lockIconPadding;
}
@@ -5059,8 +5053,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
return false;
}
- if (DeviceEntryUdfpsRefactor.isEnabled()
- && mAlternateBouncerInteractor.isVisibleState()) {
+ if (mAlternateBouncerInteractor.isVisibleState()) {
// never send touches to shade if the alternate bouncer is showing
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index cae141d014a8..5699557c14e7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -35,7 +35,6 @@ import androidx.core.view.ViewKt;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.KeyguardUnfoldTransition;
-import com.android.keyguard.LockIconViewController;
import com.android.systemui.Dumpable;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -44,7 +43,6 @@ import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags;
import com.android.systemui.bouncer.ui.binder.BouncerViewBinder;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagsClassic;
@@ -75,7 +73,6 @@ import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.DozeScrimController;
import com.android.systemui.statusbar.phone.DozeServiceHost;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
@@ -101,9 +98,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
private final NotificationShadeDepthController mDepthController;
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- private final LockIconViewController mLockIconViewController;
private final ShadeLogger mShadeLogger;
- private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final StatusBarWindowStateController mStatusBarWindowStateController;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final AmbientState mAmbientState;
@@ -174,9 +169,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
PanelExpansionInteractor panelExpansionInteractor,
ShadeExpansionStateManager shadeExpansionStateManager,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
StatusBarWindowStateController statusBarWindowStateController,
- LockIconViewController lockIconViewController,
CentralSurfaces centralSurfaces,
DozeServiceHost dozeServiceHost,
DozeScrimController dozeScrimController,
@@ -210,9 +203,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
mShadeExpansionStateManager = shadeExpansionStateManager;
mDepthController = depthController;
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
- mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mStatusBarWindowStateController = statusBarWindowStateController;
- mLockIconViewController = lockIconViewController;
mShadeLogger = shadeLogger;
mService = centralSurfaces;
mDozeServiceHost = dozeServiceHost;
@@ -259,7 +250,6 @@ public class NotificationShadeWindowViewController implements Dumpable {
mDisableSubpixelTextTransitionListener));
}
- lockIconViewController.setLockIconView(mView.findViewById(R.id.lock_icon_view));
dumpManager.registerDumpable(this);
}
@@ -392,9 +382,6 @@ public class NotificationShadeWindowViewController implements Dumpable {
}
if (mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
- // If the user was sliding their finger across the lock screen,
- // we may have been intercepting the touch and forwarding it to the
- // UDFPS affordance via mStatusBarKeyguardViewManager.onTouch (see below).
// If this touch ended up unlocking the device, we want to cancel the touch
// immediately, so we don't cause swipe or expand animations afterwards.
cancelCurrentTouch();
@@ -419,9 +406,6 @@ public class NotificationShadeWindowViewController implements Dumpable {
&& mDreamingWakeupGestureHandler.onTouchEvent(ev)) {
return logDownDispatch(ev, "dream wakeup gesture handled", true);
}
- if (mStatusBarKeyguardViewManager.dispatchTouchEvent(ev)) {
- return logDownDispatch(ev, "dispatched to Keyguard", true);
- }
if (mBrightnessMirror != null
&& mBrightnessMirror.getVisibility() == View.VISIBLE) {
// Disallow new pointers while the brightness mirror is visible. This is so that
@@ -512,7 +496,6 @@ public class NotificationShadeWindowViewController implements Dumpable {
if (mStatusBarStateController.isDozing()
&& !mDozeServiceHost.isPulsing()
&& !mDockManager.isDocked()
- && !mLockIconViewController.willHandleTouchWhileDozing(ev)
) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mShadeLogger.d("NSWVC: capture all touch events in always-on");
@@ -520,22 +503,8 @@ public class NotificationShadeWindowViewController implements Dumpable {
return true;
}
- if (mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(ev)) {
- // Don't allow touches to proceed to underlying views if alternate
- // bouncer is showing
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mShadeLogger.d("NSWVC: alt bouncer showing");
- }
- return true;
- }
-
- boolean bouncerShowing;
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing()
+ boolean bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing()
|| mAlternateBouncerInteractor.isVisibleState();
- } else {
- bouncerShowing = mService.isBouncerShowing();
- }
if (mPanelExpansionInteractor.isFullyExpanded()
&& !bouncerShowing
&& !mStatusBarStateController.isDozing()) {
@@ -603,9 +572,6 @@ public class NotificationShadeWindowViewController implements Dumpable {
if (mStatusBarStateController.isDozing()) {
handled = !mDozeServiceHost.isPulsing();
}
- if (mStatusBarKeyguardViewManager.onTouch(ev)) {
- return true;
- }
if (MigrateClocksToBlueprint.isEnabled()) {
if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) {
// we still want to finish our drag down gesture when locking the screen
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
index 949d2aa36bf3..460bfbbcb3ab 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade.domain.interactor
+import com.android.app.tracing.coroutines.flow.flowName
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
@@ -62,17 +63,20 @@ constructor(
override val isShadeEnabled: StateFlow<Boolean> =
disableFlagsRepository.disableFlags
.map { it.isShadeEnabled() }
+ .flowName("isShadeEnabled")
.stateIn(scope, SharingStarted.Eagerly, initialValue = false)
override val isQsEnabled: StateFlow<Boolean> =
disableFlagsRepository.disableFlags
.map { it.isQuickSettingsEnabled() }
+ .flowName("isQsEnabled")
.stateIn(scope, SharingStarted.Eagerly, initialValue = false)
override val isAnyFullyExpanded: StateFlow<Boolean> =
anyExpansion
.map { it >= 1f }
.distinctUntilChanged()
+ .flowName("isAnyFullyExpanded")
.stateIn(scope, SharingStarted.Eagerly, initialValue = false)
override val isShadeFullyExpanded: Flow<Boolean> =
@@ -81,6 +85,7 @@ constructor(
override val isShadeAnyExpanded: StateFlow<Boolean> =
baseShadeInteractor.shadeExpansion
.map { it > 0 }
+ .flowName("isShadeAnyExpanded")
.stateIn(scope, SharingStarted.Eagerly, false)
override val isShadeFullyCollapsed: Flow<Boolean> =
@@ -88,6 +93,7 @@ constructor(
override val isUserInteracting: StateFlow<Boolean> =
combine(isUserInteractingWithShade, isUserInteractingWithQs) { shade, qs -> shade || qs }
+ .flowName("isUserInteracting")
.stateIn(scope, SharingStarted.Eagerly, false)
override val isShadeTouchable: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
index 3113dc462e6a..4bdd36773655 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
@@ -16,7 +16,6 @@
package com.android.systemui.shade.ui.viewmodel
-import com.android.app.tracing.coroutines.flow.map
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
@@ -33,6 +32,7 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
/**
* Models the UI state for the user actions that the user can perform to navigate to other scenes.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 1481b734ff61..1ec5357d0d30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -15,7 +15,6 @@ import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
import com.android.systemui.ExpandHelper
import com.android.systemui.Gefingerpoken
-import com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy
import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
@@ -90,6 +89,7 @@ constructor(
@get:VisibleForTesting
var fractionToShade: Float = 0f
private set
+
private var useSplitShade: Boolean = false
private lateinit var nsslController: NotificationStackScrollLayoutController
lateinit var centralSurfaces: CentralSurfaces
@@ -161,9 +161,6 @@ constructor(
val distanceUntilShowingPulsingNotifications
get() = fullTransitionDistance
- /** The udfpsKeyguardViewController if it exists. */
- var mUdfpsKeyguardViewControllerLegacy: UdfpsKeyguardViewControllerLegacy? = null
-
/** The touch helper responsible for the drag down animation. */
val touchHelper =
DragDownHelper(
@@ -171,7 +168,7 @@ constructor(
this,
naturalScrollingSettingObserver,
shadeRepository,
- context
+ context,
)
private val splitShadeOverScroller: SplitShadeLockScreenOverScroller by lazy {
@@ -448,7 +445,6 @@ constructor(
val udfpsProgress = MathUtils.saturate(dragDownAmount / udfpsTransitionDistance)
shadeRepository.setUdfpsTransitionToFullShadeProgress(udfpsProgress)
- mUdfpsKeyguardViewControllerLegacy?.setTransitionToFullShadeProgress(udfpsProgress)
val statusBarProgress = MathUtils.saturate(dragDownAmount / statusBarTransitionDistance)
centralSurfaces.setTransitionToFullShadeProgress(statusBarProgress)
@@ -457,7 +453,7 @@ constructor(
private fun setDragDownAmountAnimated(
target: Float,
delay: Long = 0,
- endlistener: (() -> Unit)? = null
+ endlistener: (() -> Unit)? = null,
) {
logger.logDragDownAnimation(target)
val dragDownAnimator = ValueAnimator.ofFloat(dragDownAmount, target)
@@ -553,7 +549,7 @@ constructor(
private fun goToLockedShadeInternal(
expandView: View?,
animationHandler: ((Long) -> Unit)? = null,
- cancelAction: Runnable? = null
+ cancelAction: Runnable? = null,
) {
if (!shadeInteractor.isShadeEnabled.value) {
cancelAction?.run()
@@ -564,10 +560,7 @@ constructor(
var entry: NotificationEntry? = null
if (expandView is ExpandableNotificationRow) {
entry = expandView.entry
- entry.setUserExpanded(
- /* userExpanded= */ true,
- /* allowChildExpansion= */ true,
- )
+ entry.setUserExpanded(/* userExpanded= */ true, /* allowChildExpansion= */ true)
// Indicate that the group expansion is changing at this time -- this way the group
// and children backgrounds / divider animations will look correct.
entry.setGroupExpansionChanging(true)
@@ -594,9 +587,7 @@ constructor(
statusBarStateController.setLeaveOpenOnKeyguardHide(false)
draggedDownEntry?.apply {
setUserLocked(false)
- notifyHeightChanged(
- /* needsAnimation= */ false,
- )
+ notifyHeightChanged(/* needsAnimation= */ false)
draggedDownEntry = null
}
cancelAction?.run()
@@ -614,9 +605,7 @@ constructor(
// This call needs to be after updating the shade state since otherwise
// the scrimstate resets too early
if (animationHandler != null) {
- animationHandler.invoke(
- /* delay= */ 0,
- )
+ animationHandler.invoke(/* delay= */ 0)
} else {
performDefaultGoToFullShadeAnimation(0)
}
@@ -757,7 +746,7 @@ class DragDownHelper(
private val dragDownCallback: LockscreenShadeTransitionController,
private val naturalScrollingSettingObserver: NaturalScrollingSettingObserver,
private val shadeRepository: ShadeRepository,
- context: Context
+ context: Context,
) : Gefingerpoken {
private var dragDownAmountOnStart = 0.0f
@@ -932,7 +921,7 @@ class DragDownHelper(
@VisibleForTesting
fun cancelChildExpansion(
child: ExpandableView,
- animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS
+ animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS,
) {
if (child.actualHeight == child.collapsedHeight) {
expandCallback.setUserLockedChild(child, false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
index 32d37aef7707..c019f308d3e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -22,3 +22,7 @@ per-file *RemoteInput* = set noparent
per-file *RemoteInput* = file:notification/OWNERS
per-file *EmptyShadeView* = set noparent
per-file *EmptyShadeView* = file:notification/OWNERS
+per-file *Lockscreen* = set noparent
+per-file *Lockscreen* = file:../keyguard/OWNERS
+per-file *Scrim* = set noparent
+per-file *Scrim* = file:../keyguard/OWNERS
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
index be733d4e1e8e..8ce0dbf8e171 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
@@ -20,7 +20,7 @@ import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -31,8 +31,8 @@ import dagger.multibindings.IntoMap
abstract class StatusBarChipsModule {
@Binds
@IntoMap
- @ClassKey(DemoRonChipViewModel::class)
- abstract fun binds(impl: DemoRonChipViewModel): CoreStartable
+ @ClassKey(DemoNotifChipViewModel::class)
+ abstract fun binds(impl: DemoNotifChipViewModel): CoreStartable
companion object {
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt
index cce9a1624d51..5fa19ddef1be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
+package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel
import android.content.pm.PackageManager
import android.content.pm.PackageManager.NameNotFoundException
@@ -22,7 +22,7 @@ import android.graphics.drawable.Drawable
import com.android.systemui.CoreStartable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
@@ -37,25 +37,25 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
/**
- * A view model that will emit demo RON chips (rich ongoing notification chips) from [chip] based on
- * adb commands sent by the user.
+ * A view model that will emit demo promoted ongoing notification chips from [chip] based on adb
+ * commands sent by the user.
*
* Example adb commands:
*
* To show a chip with the SysUI icon and custom text and color:
* ```
- * adb shell cmd statusbar demo-ron -p com.android.systemui -t 10min -c "\\#434343"
+ * adb shell cmd statusbar demo-notif -p com.android.systemui -t 10min -c "\\#434343"
* ```
*
* To hide the chip:
* ```
- * adb shell cmd statusbar demo-ron --hide
+ * adb shell cmd statusbar demo-notif --hide
* ```
*
- * See [DemoRonCommand] for more information on the adb command spec.
+ * See [DemoNotifCommand] for more information on the adb command spec.
*/
@SysUISingleton
-class DemoRonChipViewModel
+class DemoNotifChipViewModel
@Inject
constructor(
private val commandRegistry: CommandRegistry,
@@ -63,19 +63,19 @@ constructor(
private val systemClock: SystemClock,
) : OngoingActivityChipViewModel, CoreStartable {
override fun start() {
- commandRegistry.registerCommand("demo-ron") { DemoRonCommand() }
+ commandRegistry.registerCommand(DEMO_COMMAND_NAME) { DemoNotifCommand() }
}
private val _chip =
MutableStateFlow<OngoingActivityChipModel>(OngoingActivityChipModel.Hidden())
override val chip: StateFlow<OngoingActivityChipModel> = _chip.asStateFlow()
- private inner class DemoRonCommand : ParseableCommand("demo-ron") {
+ private inner class DemoNotifCommand : ParseableCommand(DEMO_COMMAND_NAME) {
private val packageName: String? by
param(
longName = "packageName",
shortName = "p",
- description = "The package name for the demo RON app",
+ description = "The package name for app \"posting\" the demo notification",
valueParser = Type.String,
)
@@ -99,15 +99,12 @@ constructor(
)
private val hide by
- flag(
- longName = "hide",
- description = "Hides any existing demo RON chip",
- )
+ flag(longName = "hide", description = "Hides any existing demo notification chip")
override fun execute(pw: PrintWriter) {
- if (!StatusBarRonChips.isEnabled) {
+ if (!StatusBarNotifChips.isEnabled) {
pw.println(
- "Error: com.android.systemui.status_bar_ron_chips must be enabled " +
+ "Error: com.android.systemui.status_bar_notification_chips must be enabled " +
"before using this demo feature"
)
return
@@ -167,8 +164,12 @@ constructor(
return null
}
return OngoingActivityChipModel.ChipIcon.FullColorAppIcon(
- Icon.Loaded(drawable = iconDrawable, contentDescription = null),
+ Icon.Loaded(drawable = iconDrawable, contentDescription = null)
)
}
}
+
+ companion object {
+ private const val DEMO_COMMAND_NAME = "demo-notif"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt
index 4ef190991f19..47ffbafe3735 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.ron.shared
+package com.android.systemui.statusbar.chips.notification.shared
import com.android.systemui.Flags
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
-/** Helper for reading or using the status bar RON chips flag state. */
+/** Helper for reading or using the status bar promoted notification chips flag state. */
@Suppress("NOTHING_TO_INLINE")
-object StatusBarRonChips {
+object StatusBarNotifChips {
/** The aconfig flag name */
- const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS
+ const val FLAG_NAME = Flags.FLAG_STATUS_BAR_NOTIFICATION_CHIPS
/** A token used for dependency declaration */
val token: FlagToken
@@ -33,7 +33,7 @@ object StatusBarRonChips {
/** Is the refactor enabled */
@JvmStatic
inline val isEnabled
- get() = Flags.statusBarRonChips()
+ get() = Flags.statusBarNotificationChips()
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
new file mode 100644
index 000000000000..6ae92637bde7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.systemui.statusbar.chips.notification.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.chips.ui.model.ColorsModel
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
+import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** A view model for status bar chips for promoted ongoing notifications. */
+@SysUISingleton
+class NotifChipsViewModel
+@Inject
+constructor(activeNotificationsInteractor: ActiveNotificationsInteractor) {
+ /**
+ * A flow modeling the notification chips that should be shown. Emits an empty list if there are
+ * no notifications that should show a status bar chip.
+ */
+ val chips: Flow<List<OngoingActivityChipModel.Shown>> =
+ activeNotificationsInteractor.promotedOngoingNotifications.map { notifications ->
+ notifications.mapNotNull { it.toChipModel() }
+ }
+
+ /**
+ * Converts the notification to the [OngoingActivityChipModel] object. Returns null if the
+ * notification has invalid data such that it can't be displayed as a chip.
+ */
+ private fun ActiveNotificationModel.toChipModel(): OngoingActivityChipModel.Shown? {
+ // TODO(b/364653005): Log error if there's no icon view.
+ val rawIcon = this.statusBarChipIconView ?: return null
+ val icon = OngoingActivityChipModel.ChipIcon.StatusBarView(rawIcon)
+ // TODO(b/364653005): Use the notification color if applicable.
+ val colors = ColorsModel.Themed
+ // TODO(b/364653005): When the chip is clicked, show the HUN.
+ val onClickListener = null
+ return OngoingActivityChipModel.Shown.ShortTimeDelta(
+ icon,
+ colors,
+ time = this.whenTime,
+ onClickListener,
+ )
+ // TODO(b/364653005): If Notification.showWhen = false, don't show the time delta.
+ // TODO(b/364653005): If Notification.whenTime is in the past, show "ago" in the text.
+ // TODO(b/364653005): If Notification.shortCriticalText is set, use that instead of `when`.
+ // TODO(b/364653005): If the app that posted the notification is in the foreground, don't
+ // show that app's chip.
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index 3b1e565e122b..f4462a434477 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -21,13 +21,14 @@ import android.content.res.ColorStateList
import android.graphics.drawable.GradientDrawable
import android.view.View
import android.view.ViewGroup
+import android.widget.DateTimeView
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
@@ -42,6 +43,8 @@ object OngoingActivityChipBinder {
val chipTimeView: ChipChronometer =
chipView.requireViewById(R.id.ongoing_activity_chip_time)
val chipTextView: TextView = chipView.requireViewById(R.id.ongoing_activity_chip_text)
+ val chipShortTimeDeltaView: DateTimeView =
+ chipView.requireViewById(R.id.ongoing_activity_chip_short_time_delta)
val chipBackgroundView: ChipBackgroundContainer =
chipView.requireViewById(R.id.ongoing_activity_chip_background)
@@ -49,13 +52,14 @@ object OngoingActivityChipBinder {
is OngoingActivityChipModel.Shown -> {
// Data
setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView)
- setChipMainContent(chipModel, chipTextView, chipTimeView)
+ setChipMainContent(chipModel, chipTextView, chipTimeView, chipShortTimeDeltaView)
chipView.setOnClickListener(chipModel.onClickListener)
updateChipPadding(
chipModel,
chipBackgroundView,
chipTextView,
chipTimeView,
+ chipShortTimeDeltaView,
)
// Accessibility
@@ -65,6 +69,7 @@ object OngoingActivityChipBinder {
val textColor = chipModel.colors.text(chipContext)
chipTimeView.setTextColor(textColor)
chipTextView.setTextColor(textColor)
+ chipShortTimeDeltaView.setTextColor(textColor)
(chipBackgroundView.background as GradientDrawable).color =
chipModel.colors.background(chipContext)
}
@@ -97,7 +102,7 @@ object OngoingActivityChipBinder {
defaultIconView.tintView(iconTint)
}
is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> {
- StatusBarRonChips.assertInNewMode()
+ StatusBarNotifChips.assertInNewMode()
IconViewBinder.bind(icon.impl, defaultIconView)
defaultIconView.visibility = View.VISIBLE
defaultIconView.untintView()
@@ -161,6 +166,7 @@ object OngoingActivityChipBinder {
chipModel: OngoingActivityChipModel.Shown,
chipTextView: TextView,
chipTimeView: ChipChronometer,
+ chipShortTimeDeltaView: DateTimeView,
) {
when (chipModel) {
is OngoingActivityChipModel.Shown.Countdown -> {
@@ -168,21 +174,35 @@ object OngoingActivityChipBinder {
chipTextView.visibility = View.VISIBLE
chipTimeView.hide()
+ chipShortTimeDeltaView.visibility = View.GONE
}
is OngoingActivityChipModel.Shown.Text -> {
chipTextView.text = chipModel.text
chipTextView.visibility = View.VISIBLE
chipTimeView.hide()
+ chipShortTimeDeltaView.visibility = View.GONE
}
is OngoingActivityChipModel.Shown.Timer -> {
ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
chipTimeView.visibility = View.VISIBLE
chipTextView.visibility = View.GONE
+ chipShortTimeDeltaView.visibility = View.GONE
+ }
+ is OngoingActivityChipModel.Shown.ShortTimeDelta -> {
+ chipShortTimeDeltaView.setTime(chipModel.time)
+ // TODO(b/364653005): DateTimeView's relative time doesn't quite match the format we
+ // want in the status bar chips.
+ chipShortTimeDeltaView.isShowRelativeTime = true
+ chipShortTimeDeltaView.visibility = View.VISIBLE
+
+ chipTextView.visibility = View.GONE
+ chipTimeView.hide()
}
is OngoingActivityChipModel.Shown.IconOnly -> {
chipTextView.visibility = View.GONE
+ chipShortTimeDeltaView.visibility = View.GONE
chipTimeView.hide()
}
}
@@ -200,6 +220,7 @@ object OngoingActivityChipBinder {
backgroundView: View,
chipTextView: TextView,
chipTimeView: ChipChronometer,
+ chipShortTimeDeltaView: DateTimeView,
) {
if (chipModel.icon != null) {
if (chipModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView) {
@@ -209,15 +230,18 @@ object OngoingActivityChipBinder {
backgroundView.setBackgroundPaddingForEmbeddedPaddingIcon()
chipTextView.setTextPaddingForEmbeddedPaddingIcon()
chipTimeView.setTextPaddingForEmbeddedPaddingIcon()
+ chipShortTimeDeltaView.setTextPaddingForEmbeddedPaddingIcon()
} else {
backgroundView.setBackgroundPaddingForNormalIcon()
chipTextView.setTextPaddingForNormalIcon()
chipTimeView.setTextPaddingForNormalIcon()
+ chipShortTimeDeltaView.setTextPaddingForNormalIcon()
}
} else {
backgroundView.setBackgroundPaddingForNoIcon()
chipTextView.setTextPaddingForNoIcon()
chipTimeView.setTextPaddingForNoIcon()
+ chipShortTimeDeltaView.setTextPaddingForNoIcon()
}
}
@@ -258,23 +282,13 @@ object OngoingActivityChipBinder {
context.resources.getDimensionPixelSize(
R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
)
- setPaddingRelative(
- sidePadding,
- paddingTop,
- sidePadding,
- paddingBottom,
- )
+ setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom)
}
private fun View.setBackgroundPaddingForNormalIcon() {
val sidePadding =
context.resources.getDimensionPixelSize(R.dimen.ongoing_activity_chip_side_padding)
- setPaddingRelative(
- sidePadding,
- paddingTop,
- sidePadding,
- paddingBottom,
- )
+ setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom)
}
private fun View.setBackgroundPaddingForNoIcon() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index 62622a893ec5..cf07af1fc5dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -20,6 +20,7 @@ import android.view.View
import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
/** Model representing the display of an ongoing activity as a chip in the status bar. */
sealed class OngoingActivityChipModel {
@@ -79,6 +80,24 @@ sealed class OngoingActivityChipModel {
}
/**
+ * The chip shows the time delta between now and [time] in a short format, e.g. "15min" or
+ * "1hr ago".
+ */
+ data class ShortTimeDelta(
+ override val icon: ChipIcon,
+ override val colors: ColorsModel,
+ /** The time of the event that this chip represents. */
+ val time: Long,
+ override val onClickListener: View.OnClickListener?,
+ ) : Shown(icon, colors, onClickListener) {
+ init {
+ StatusBarNotifChips.assertInNewMode()
+ }
+
+ override val logName = "Shown.ShortTimeDelta"
+ }
+
+ /**
* This chip shows a countdown using [secondsUntilStarted]. Used to inform users that an
* event is about to start. Typically, a [Countdown] chip will turn into a [Timer] chip.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index 24c1b879f429..ed325970ebb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.chips.ui.viewmodel
-import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
@@ -24,19 +23,20 @@ import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel
import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel
-import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel
import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel
import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.util.kotlin.combine
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -55,7 +55,8 @@ constructor(
shareToAppChipViewModel: ShareToAppChipViewModel,
castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel,
callChipViewModel: CallChipViewModel,
- demoRonChipViewModel: DemoRonChipViewModel,
+ notifChipsViewModel: NotifChipsViewModel,
+ demoNotifChipViewModel: DemoNotifChipViewModel,
@StatusBarChipsLog private val logger: LogBuffer,
) {
private enum class ChipType {
@@ -63,8 +64,9 @@ constructor(
ShareToApp,
CastToOtherDevice,
Call,
- /** A demo of a RON chip (rich ongoing notification chip), used just for testing. */
- DemoRon,
+ Notification,
+ /** A demo of a notification chip, used just for testing. */
+ DemoNotification,
}
/** Model that helps us internally track the various chip states from each of the types. */
@@ -85,7 +87,8 @@ constructor(
val shareToApp: OngoingActivityChipModel.Hidden,
val castToOtherDevice: OngoingActivityChipModel.Hidden,
val call: OngoingActivityChipModel.Hidden,
- val demoRon: OngoingActivityChipModel.Hidden,
+ val notifs: OngoingActivityChipModel.Hidden,
+ val demoNotif: OngoingActivityChipModel.Hidden,
) : InternalChipModel
}
@@ -94,7 +97,8 @@ constructor(
val shareToApp: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
val castToOtherDevice: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
val call: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
- val demoRon: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
+ val notifs: List<OngoingActivityChipModel.Shown> = emptyList(),
+ val demoNotif: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
)
/** Bundles all the incoming chips into one object to easily pass to various flows. */
@@ -104,8 +108,9 @@ constructor(
shareToAppChipViewModel.chip,
castToOtherDeviceChipViewModel.chip,
callChipViewModel.chip,
- demoRonChipViewModel.chip,
- ) { screenRecord, shareToApp, castToOtherDevice, call, demoRon ->
+ notifChipsViewModel.chips,
+ demoNotifChipViewModel.chip,
+ ) { screenRecord, shareToApp, castToOtherDevice, call, notifs, demoNotif ->
logger.log(
TAG,
LogLevel.INFO,
@@ -121,16 +126,19 @@ constructor(
LogLevel.INFO,
{
str1 = call.logName
- str2 = demoRon.logName
+ // TODO(b/364653005): Log other information for notification chips.
+ str2 = notifs.map { it.logName }.toString()
+ str3 = demoNotif.logName
},
- { "... > Call=$str1 > DemoRon=$str2" }
+ { "... > Call=$str1 > Notifs=$str2 > DemoNotif=$str3" },
)
ChipBundle(
screenRecord = screenRecord,
shareToApp = shareToApp,
castToOtherDevice = castToOtherDevice,
call = call,
- demoRon = demoRon,
+ notifs = notifs,
+ demoNotif = demoNotif,
)
}
// Some of the chips could have timers in them and we don't want the start time
@@ -189,9 +197,9 @@ constructor(
* actually displaying the chip.
*/
val chips: StateFlow<MultipleOngoingActivityChipsModel> =
- if (!Flags.statusBarRonChips()) {
- // Multiple chips are only allowed with RONs. If the flag isn't on, use just the
- // primary chip.
+ if (!StatusBarNotifChips.isEnabled) {
+ // Multiple chips are only allowed with notification chips. If the flag isn't on, use
+ // just the primary chip.
primaryChip
.map {
MultipleOngoingActivityChipsModel(
@@ -199,11 +207,7 @@ constructor(
secondary = OngoingActivityChipModel.Hidden(),
)
}
- .stateIn(
- scope,
- SharingStarted.Lazily,
- MultipleOngoingActivityChipsModel(),
- )
+ .stateIn(scope, SharingStarted.Lazily, MultipleOngoingActivityChipsModel())
} else {
internalChips
.pairwise(initialValue = DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL)
@@ -212,11 +216,7 @@ constructor(
val correctSecondary = createOutputModel(old.secondary, new.secondary)
MultipleOngoingActivityChipsModel(correctPrimary, correctSecondary)
}
- .stateIn(
- scope,
- SharingStarted.Lazily,
- MultipleOngoingActivityChipsModel(),
- )
+ .stateIn(scope, SharingStarted.Lazily, MultipleOngoingActivityChipsModel())
}
/** A data class representing the return result of [pickMostImportantChip]. */
@@ -251,7 +251,7 @@ constructor(
// suppress the share-to-app chip to make sure they don't both show.
// See b/296461748.
shareToApp = OngoingActivityChipModel.Hidden(),
- )
+ ),
)
bundle.shareToApp is OngoingActivityChipModel.Shown ->
MostImportantChipResult(
@@ -274,11 +274,19 @@ constructor(
mostImportantChip = InternalChipModel.Shown(ChipType.Call, bundle.call),
remainingChips = bundle.copy(call = OngoingActivityChipModel.Hidden()),
)
- bundle.demoRon is OngoingActivityChipModel.Shown -> {
- StatusBarRonChips.assertInNewMode()
+ bundle.notifs.isNotEmpty() ->
+ MostImportantChipResult(
+ mostImportantChip =
+ InternalChipModel.Shown(ChipType.Notification, bundle.notifs.first()),
+ remainingChips =
+ bundle.copy(notifs = bundle.notifs.subList(1, bundle.notifs.size)),
+ )
+ bundle.demoNotif is OngoingActivityChipModel.Shown -> {
+ StatusBarNotifChips.assertInNewMode()
MostImportantChipResult(
- mostImportantChip = InternalChipModel.Shown(ChipType.DemoRon, bundle.demoRon),
- remainingChips = bundle.copy(demoRon = OngoingActivityChipModel.Hidden()),
+ mostImportantChip =
+ InternalChipModel.Shown(ChipType.DemoNotification, bundle.demoNotif),
+ remainingChips = bundle.copy(demoNotif = OngoingActivityChipModel.Hidden()),
)
}
else -> {
@@ -287,7 +295,8 @@ constructor(
check(bundle.shareToApp is OngoingActivityChipModel.Hidden)
check(bundle.castToOtherDevice is OngoingActivityChipModel.Hidden)
check(bundle.call is OngoingActivityChipModel.Hidden)
- check(bundle.demoRon is OngoingActivityChipModel.Hidden)
+ check(bundle.notifs.isEmpty())
+ check(bundle.demoNotif is OngoingActivityChipModel.Hidden)
MostImportantChipResult(
mostImportantChip =
InternalChipModel.Hidden(
@@ -295,7 +304,8 @@ constructor(
shareToApp = bundle.shareToApp,
castToOtherDevice = bundle.castToOtherDevice,
call = bundle.call,
- demoRon = bundle.demoRon,
+ notifs = OngoingActivityChipModel.Hidden(),
+ demoNotif = bundle.demoNotif,
),
// All the chips are already hidden, so no need to filter anything out of the
// bundle.
@@ -323,7 +333,8 @@ constructor(
ChipType.ShareToApp -> new.shareToApp
ChipType.CastToOtherDevice -> new.castToOtherDevice
ChipType.Call -> new.call
- ChipType.DemoRon -> new.demoRon
+ ChipType.Notification -> new.notifs
+ ChipType.DemoNotification -> new.demoNotif
}
} else if (new is InternalChipModel.Shown) {
// If we have a chip to show, always show it.
@@ -344,7 +355,8 @@ constructor(
shareToApp = OngoingActivityChipModel.Hidden(),
castToOtherDevice = OngoingActivityChipModel.Hidden(),
call = OngoingActivityChipModel.Hidden(),
- demoRon = OngoingActivityChipModel.Hidden(),
+ notifs = OngoingActivityChipModel.Hidden(),
+ demoNotif = OngoingActivityChipModel.Hidden(),
)
private val DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
new file mode 100644
index 000000000000..b64a577ef77e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.systemui.statusbar.core
+
+import android.view.Display
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.data.repository.DisplayScopeRepository
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
+import com.android.systemui.util.kotlin.pairwiseBy
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
+
+/**
+ * Responsible for creating and starting the status bar components for each display. Also does it
+ * for newly added displays.
+ */
+@SysUISingleton
+class MultiDisplayStatusBarStarter
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val displayScopeRepository: DisplayScopeRepository,
+ private val statusBarOrchestratorFactory: StatusBarOrchestrator.Factory,
+ private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+ private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+ private val displayRepository: DisplayRepository,
+ private val initializerStore: StatusBarInitializerStore,
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ private val statusBarInitializerStore: StatusBarInitializerStore,
+) : CoreStartable {
+
+ init {
+ StatusBarConnectedDisplays.assertInNewMode()
+ }
+
+ override fun start() {
+ applicationScope.launch {
+ displayRepository.displays
+ .pairwiseBy { previousDisplays, currentDisplays ->
+ currentDisplays - previousDisplays
+ }
+ .onStart { emit(displayRepository.displays.value) }
+ .collect { newDisplays ->
+ newDisplays.forEach { createAndStartComponentsForDisplay(it) }
+ }
+ }
+ }
+
+ private fun createAndStartComponentsForDisplay(display: Display) {
+ val displayId = display.displayId
+ createAndStartOrchestratorForDisplay(displayId)
+ createAndStartInitializerForDisplay(displayId)
+ }
+
+ private fun createAndStartOrchestratorForDisplay(displayId: Int) {
+ statusBarOrchestratorFactory
+ .create(
+ displayId,
+ displayScopeRepository.scopeForDisplay(displayId),
+ statusBarWindowStateRepositoryStore.forDisplay(displayId),
+ statusBarModeRepositoryStore.forDisplay(displayId),
+ initializerStore.forDisplay(displayId),
+ statusBarWindowControllerStore.forDisplay(displayId),
+ )
+ .start()
+ }
+
+ private fun createAndStartInitializerForDisplay(displayId: Int) {
+ statusBarInitializerStore.forDisplay(displayId).start()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
index 7eff8124368b..2c94632abcda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
@@ -26,7 +26,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
-import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.StatusBarWindowController
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -37,7 +37,7 @@ import javax.inject.Provider
* Responsible for creating the status bar window and initializing the root components of that
* window (see [CollapsedStatusBarFragment])
*/
-interface StatusBarInitializer {
+interface StatusBarInitializer : CoreStartable {
var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener?
@@ -68,18 +68,17 @@ interface StatusBarInitializer {
}
interface Factory {
- fun create(displayId: Int): StatusBarInitializer
+ fun create(statusBarWindowController: StatusBarWindowController): StatusBarInitializer
}
}
class StatusBarInitializerImpl
@AssistedInject
constructor(
- @Assisted private val displayId: Int,
- private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ @Assisted private val statusBarWindowController: StatusBarWindowController,
private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>,
private val creationListeners: Set<@JvmSuppressWildcards OnStatusBarViewInitializedListener>,
-) : CoreStartable, StatusBarInitializer {
+) : StatusBarInitializer {
private var component: StatusBarFragmentComponent? = null
@get:VisibleForTesting
@@ -111,7 +110,7 @@ constructor(
private fun doStart() {
initialized = true
- statusBarWindowControllerStore.defaultDisplay.fragmentHostManager
+ statusBarWindowController.fragmentHostManager
.addTagListener(
CollapsedStatusBarFragment.TAG,
object : FragmentHostManager.FragmentListener {
@@ -145,6 +144,8 @@ constructor(
@AssistedFactory
interface Factory : StatusBarInitializer.Factory {
- override fun create(displayId: Int): StatusBarInitializerImpl
+ override fun create(
+ statusBarWindowController: StatusBarWindowController
+ ): StatusBarInitializerImpl
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
index 8d044bb9ce87..041f0b0fdf93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
@@ -16,84 +16,52 @@
package com.android.systemui.statusbar.core
-import android.view.Display
-import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
-import java.util.concurrent.ConcurrentHashMap
+import com.android.systemui.display.data.repository.PerDisplayStore
+import com.android.systemui.display.data.repository.PerDisplayStoreImpl
+import com.android.systemui.display.data.repository.SingleDisplayStore
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
/** Provides per display instances of [StatusBarInitializer]. */
-interface StatusBarInitializerStore {
- /**
- * The instance for the default/main display of the device. For example, on a phone or a tablet,
- * the default display is the internal/built-in display of the device.
- *
- * Note that the id of the default display is [Display.DEFAULT_DISPLAY].
- */
- val defaultDisplay: StatusBarInitializer
-
- /**
- * Returns an instance for a specific display id.
- *
- * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing
- * displays.
- */
- fun forDisplay(displayId: Int): StatusBarInitializer
-}
+interface StatusBarInitializerStore : PerDisplayStore<StatusBarInitializer>
@SysUISingleton
class MultiDisplayStatusBarInitializerStore
@Inject
constructor(
- @Background private val backgroundApplicationScope: CoroutineScope,
+ @Background backgroundApplicationScope: CoroutineScope,
+ displayRepository: DisplayRepository,
private val factory: StatusBarInitializer.Factory,
- private val displayRepository: DisplayRepository,
-) : StatusBarInitializerStore, CoreStartable {
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+) :
+ StatusBarInitializerStore,
+ PerDisplayStoreImpl<StatusBarInitializer>(backgroundApplicationScope, displayRepository) {
init {
StatusBarConnectedDisplays.assertInNewMode()
}
- private val perDisplayInitializers = ConcurrentHashMap<Int, StatusBarInitializer>()
-
- override val defaultDisplay: StatusBarInitializer
- get() = forDisplay(Display.DEFAULT_DISPLAY)
-
- override fun forDisplay(displayId: Int): StatusBarInitializer {
- if (displayRepository.getDisplay(displayId) == null) {
- throw IllegalArgumentException("Display with id $displayId doesn't exist.")
- }
- return perDisplayInitializers.computeIfAbsent(displayId) { factory.create(displayId) }
+ override fun createInstanceForDisplay(displayId: Int): StatusBarInitializer {
+ return factory.create(
+ statusBarWindowController = statusBarWindowControllerStore.forDisplay(displayId)
+ )
}
- override fun start() {
- backgroundApplicationScope.launch(
- CoroutineName("MultiDisplayStatusBarInitializerStore#start")
- ) {
- displayRepository.displayRemovalEvent.collect { removedDisplayId ->
- perDisplayInitializers.remove(removedDisplayId)
- }
- }
- }
+ override val instanceClass = StatusBarInitializer::class.java
}
@SysUISingleton
class SingleDisplayStatusBarInitializerStore
@Inject
-constructor(factory: StatusBarInitializerImpl.Factory) : StatusBarInitializerStore {
+constructor(defaultInitializer: StatusBarInitializer) :
+ StatusBarInitializerStore,
+ PerDisplayStore<StatusBarInitializer> by SingleDisplayStore(defaultInitializer) {
init {
StatusBarConnectedDisplays.assertInLegacyMode()
}
-
- private val defaultInstance = factory.create(Display.DEFAULT_DISPLAY)
-
- override val defaultDisplay: StatusBarInitializer = defaultInstance
-
- override fun forDisplay(displayId: Int): StatusBarInitializer = defaultDisplay
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
index d372eb29b27b..47e6c57a5ca7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
@@ -16,12 +16,12 @@
package com.android.systemui.statusbar.core
+import android.view.Display
import android.view.View
import com.android.systemui.CoreStartable
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.plugins.PluginDependencyProvider
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -31,19 +31,21 @@ import com.android.systemui.shade.ShadeSurface
import com.android.systemui.statusbar.AutoHideUiElement
import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.data.model.StatusBarMode
-import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository
import com.android.systemui.statusbar.phone.AutoHideController
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
-import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
-import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository
import com.android.wm.shell.bubbles.Bubbles
import dagger.Lazy
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import java.io.PrintWriter
import java.util.Optional
-import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
@@ -57,27 +59,35 @@ import kotlinx.coroutines.launch
* It is a temporary class, created to pull status bar related logic out of CentralSurfacesImpl. The
* plan is break it out into individual classes.
*/
-@SysUISingleton
class StatusBarOrchestrator
-@Inject
+@AssistedInject
constructor(
- @Application private val applicationScope: CoroutineScope,
- private val statusBarInitializer: StatusBarInitializer,
- private val statusBarModeRepository: StatusBarModeRepositoryStore,
- private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ @Assisted private val displayId: Int,
+ @Assisted private val coroutineScope: CoroutineScope,
+ @Assisted private val statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository,
+ @Assisted private val statusBarModeRepository: StatusBarModePerDisplayRepository,
+ @Assisted private val statusBarInitializer: StatusBarInitializer,
+ @Assisted private val statusBarWindowController: StatusBarWindowController,
private val demoModeController: DemoModeController,
private val pluginDependencyProvider: PluginDependencyProvider,
private val autoHideController: AutoHideController,
private val remoteInputManager: NotificationRemoteInputManager,
private val notificationShadeWindowViewControllerLazy:
- Lazy<NotificationShadeWindowViewController>,
+ Lazy<NotificationShadeWindowViewController>,
private val shadeSurface: ShadeSurface,
private val bubblesOptional: Optional<Bubbles>,
- private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+ private val dumpManager: DumpManager,
powerInteractor: PowerInteractor,
primaryBouncerInteractor: PrimaryBouncerInteractor,
) : CoreStartable {
+ private val dumpableName: String =
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ javaClass.simpleName
+ } else {
+ "${javaClass.simpleName}$displayId"
+ }
+
private val phoneStatusBarViewController =
MutableStateFlow<PhoneStatusBarViewController?>(value = null)
@@ -86,9 +96,9 @@ constructor(
private val shouldAnimateNextBarModeChange =
combine(
- statusBarModeRepository.defaultDisplay.isTransientShown,
+ statusBarModeRepository.isTransientShown,
powerInteractor.isAwake,
- statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
+ statusBarWindowStateRepository.windowState,
) { isTransientShown, isDeviceAwake, statusBarWindowState ->
!isTransientShown &&
isDeviceAwake &&
@@ -107,8 +117,8 @@ constructor(
private val statusBarVisible =
combine(
- statusBarModeRepository.defaultDisplay.statusBarMode,
- statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
+ statusBarModeRepository.statusBarMode,
+ statusBarWindowStateRepository.windowState,
) { mode, statusBarWindowState ->
mode != StatusBarMode.LIGHTS_OUT &&
mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT &&
@@ -119,7 +129,7 @@ constructor(
combine(
shouldAnimateNextBarModeChange,
phoneStatusBarTransitions.filterNotNull(),
- statusBarModeRepository.defaultDisplay.statusBarMode,
+ statusBarModeRepository.statusBarMode,
::Triple,
)
.distinctUntilChangedBy { (_, barTransitions, statusBarMode) ->
@@ -130,26 +140,29 @@ constructor(
override fun start() {
StatusBarSimpleFragment.assertInNewMode()
- applicationScope.launch {
- launch {
- controllerAndBouncerShowing.collect { (controller, bouncerShowing) ->
- setBouncerShowingForStatusBarComponents(controller, bouncerShowing)
+ coroutineScope
+ .launch {
+ dumpManager.registerCriticalDumpable(dumpableName, this@StatusBarOrchestrator)
+ launch {
+ controllerAndBouncerShowing.collect { (controller, bouncerShowing) ->
+ setBouncerShowingForStatusBarComponents(controller, bouncerShowing)
+ }
}
- }
- launch {
- barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) ->
- if (deviceAsleep) {
- barTransitions.finishAnimations()
+ launch {
+ barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) ->
+ if (deviceAsleep) {
+ barTransitions.finishAnimations()
+ }
}
}
- }
- launch { statusBarVisible.collect { updateBubblesVisibility(it) } }
- launch {
- barModeUpdate.collect { (animate, barTransitions, statusBarMode) ->
- updateBarMode(animate, barTransitions, statusBarMode)
+ launch { statusBarVisible.collect { updateBubblesVisibility(it) } }
+ launch {
+ barModeUpdate.collect { (animate, barTransitions, statusBarMode) ->
+ updateBarMode(animate, barTransitions, statusBarMode)
+ }
}
}
- }
+ .invokeOnCompletion { dumpManager.unregisterDumpable(dumpableName) }
createAndAddWindow()
setupPluginDependencies()
setUpAutoHide()
@@ -157,7 +170,7 @@ constructor(
private fun createAndAddWindow() {
initializeStatusBarFragment()
- statusBarWindowControllerStore.defaultDisplay.attach()
+ statusBarWindowController.attach()
}
private fun initializeStatusBarFragment() {
@@ -170,6 +183,10 @@ constructor(
phoneStatusBarViewController.value = statusBarViewController
phoneStatusBarTransitions.value = statusBarTransitions
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ return
+ }
+ // TODO(b/373310629): shade should be display id aware
notificationShadeWindowViewControllerLazy
.get()
.setStatusBarViewController(statusBarViewController)
@@ -189,6 +206,10 @@ constructor(
}
private fun setUpAutoHide() {
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ return
+ }
+ // TODO(b/373309973): per display implementation of auto hide controller
autoHideController.setStatusBar(
object : AutoHideUiElement {
override fun synchronizeState() {}
@@ -198,13 +219,14 @@ constructor(
}
override fun isVisible(): Boolean {
- return statusBarModeRepository.defaultDisplay.isTransientShown.value
+ return statusBarModeRepository.isTransientShown.value
}
override fun hide() {
- statusBarModeRepository.defaultDisplay.clearTransient()
+ statusBarModeRepository.clearTransient()
}
- })
+ }
+ )
}
private fun updateBarMode(
@@ -215,11 +237,18 @@ constructor(
if (!demoModeController.isInDemoMode) {
barTransitions.transitionTo(barMode.toTransitionModeInt(), animate)
}
- autoHideController.touchAutoHide()
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ // TODO(b/373309973): per display implementation of auto hide controller
+ autoHideController.touchAutoHide()
+ }
}
private fun updateBubblesVisibility(statusBarVisible: Boolean) {
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ return
+ }
bubblesOptional.ifPresent { bubbles: Bubbles ->
+ // TODO(b/373311537): per display implementation of Bubbles
bubbles.onStatusBarVisibilityChanged(statusBarVisible)
}
}
@@ -238,11 +267,23 @@ constructor(
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println(statusBarWindowStateRepositoryStore.defaultDisplay.windowState.value)
+ pw.println(statusBarWindowStateRepository.windowState.value)
CentralSurfaces.dumpBarTransitions(
pw,
"PhoneStatusBarTransitions",
phoneStatusBarTransitions.value,
)
}
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ displayId: Int,
+ displayScope: CoroutineScope,
+ statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository,
+ statusBarModeRepository: StatusBarModePerDisplayRepository,
+ statusBarInitializer: StatusBarInitializer,
+ statusBarWindowController: StatusBarWindowController,
+ ): StatusBarOrchestrator
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
index 5aad11fe1034..f6f4503b210a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -17,17 +17,20 @@
package com.android.systemui.statusbar.dagger
import android.content.Context
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.CoreStartable
+import com.android.systemui.SysUICutoutProvider
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.data.StatusBarDataLayerModule
import com.android.systemui.statusbar.phone.LightBarController
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.ui.SystemBarUtilsProxyImpl
import com.android.systemui.statusbar.window.MultiDisplayStatusBarWindowControllerStore
import com.android.systemui.statusbar.window.SingleDisplayStatusBarWindowControllerStore
@@ -108,5 +111,16 @@ abstract class StatusBarModule {
fun provideOngoingCallLogBuffer(factory: LogBufferFactory): LogBuffer {
return factory.create("OngoingCall", 75)
}
+
+ @Provides
+ @SysUISingleton
+ fun contentInsetsProvider(
+ factory: StatusBarContentInsetsProviderImpl.Factory,
+ context: Context,
+ configurationController: ConfigurationController,
+ sysUICutoutProvider: SysUICutoutProvider,
+ ): StatusBarContentInsetsProvider {
+ return factory.create(context, configurationController, sysUICutoutProvider)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
index 962cb0953f97..154be1f96e8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
@@ -31,6 +31,7 @@ import javax.inject.Inject
interface StatusBarModeRepositoryStore {
val defaultDisplay: StatusBarModePerDisplayRepository
+
fun forDisplay(displayId: Int): StatusBarModePerDisplayRepository
}
@@ -39,7 +40,7 @@ class StatusBarModeRepositoryImpl
@Inject
constructor(
@DisplayId private val displayId: Int,
- factory: StatusBarModePerDisplayRepositoryFactory
+ factory: StatusBarModePerDisplayRepositoryFactory,
) :
StatusBarModeRepositoryStore,
CoreStartable,
@@ -47,11 +48,9 @@ constructor(
override val defaultDisplay = factory.create(displayId)
override fun forDisplay(displayId: Int) =
- if (this.displayId == displayId) {
- defaultDisplay
- } else {
- TODO("b/127878649 implement multi-display state management")
- }
+ // TODO(b/369337087): implement per display status bar modes.
+ // For now just use default display instance.
+ defaultDisplay
override fun start() {
defaultDisplay.start()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java
index 0927a728783f..a61463823613 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpTouchHelper.java
@@ -115,23 +115,23 @@ public class HeadsUpTouchHelper implements Gefingerpoken {
final float h = y - mInitialTouchY;
if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop
&& Math.abs(h) > Math.abs(x - mInitialTouchX)) {
- setTrackingHeadsUp(true);
- mCollapseSnoozes = h < 0;
- mInitialTouchX = x;
- mInitialTouchY = y;
- int startHeight = (int) (mPickedChild.getActualHeight()
- + mPickedChild.getTranslationY());
- mPanel.setHeadsUpDraggingStartingHeight(startHeight);
- mPanel.startExpand(x, y, true /* startTracking */, startHeight);
-
if (!SceneContainerFlag.isEnabled()) {
+ setTrackingHeadsUp(true);
+ mCollapseSnoozes = h < 0;
+ mInitialTouchX = x;
+ mInitialTouchY = y;
+ int startHeight = (int) (mPickedChild.getActualHeight()
+ + mPickedChild.getTranslationY());
+ mPanel.setHeadsUpDraggingStartingHeight(startHeight);
+ mPanel.startExpand(x, y, true /* startTracking */, startHeight);
+
// This call needs to be after the expansion start otherwise we will get a
// flicker of one frame as it's not expanded yet.
mHeadsUpManager.unpinAll(true);
- }
- clearNotificationEffects();
- endMotion();
+ clearNotificationEffects();
+ endMotion();
+ }
return true;
}
break;
@@ -167,17 +167,70 @@ public class HeadsUpTouchHelper implements Gefingerpoken {
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (!mTrackingHeadsUp) {
+ if (SceneContainerFlag.isEnabled()) {
+ int pointerIndex = event.findPointerIndex(mTrackingPointer);
+ if (pointerIndex < 0) {
+ pointerIndex = 0;
+ mTrackingPointer = event.getPointerId(pointerIndex);
+ }
+ final float x = event.getX(pointerIndex);
+ final float y = event.getY(pointerIndex);
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_POINTER_UP:
+ final int upPointer = event.getPointerId(event.getActionIndex());
+ if (mTrackingPointer == upPointer) {
+ // gesture is ongoing, find a new pointer to track
+ final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+ mTrackingPointer = event.getPointerId(newIndex);
+ mInitialTouchX = event.getX(newIndex);
+ mInitialTouchY = event.getY(newIndex);
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ final float h = y - mInitialTouchY;
+ if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop
+ && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
+ setTrackingHeadsUp(true);
+ mCollapseSnoozes = h < 0;
+ mInitialTouchX = x;
+ mInitialTouchY = y;
+ int startHeight = (int) (mPickedChild.getActualHeight()
+ + mPickedChild.getTranslationY());
+ mPanel.setHeadsUpDraggingStartingHeight(startHeight);
+ mPanel.startExpand(x, y, true /* startTracking */, startHeight);
+
+ clearNotificationEffects();
+ endMotion();
+ return true;
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ if (mPickedChild != null && mTouchingHeadsUpView) {
+ // We may swallow this click if the heads up just came in.
+ if (mHeadsUpManager.shouldSwallowClick(
+ mPickedChild.getEntry().getSbn().getKey())) {
+ endMotion();
+ return true;
+ }
+ }
+ endMotion();
+ return false;
+ }
return false;
+ } else {
+ if (!mTrackingHeadsUp) {
+ return false;
+ }
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ endMotion();
+ setTrackingHeadsUp(false);
+ break;
+ }
+ return true;
}
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- endMotion();
- setTrackingHeadsUp(false);
- break;
- }
- return true;
}
private void endMotion() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 9b382e61b2e3..697a6ce52ba9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
@@ -26,6 +27,7 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -74,6 +76,17 @@ constructor(
val allNotificationsCountValue: Int
get() = repository.activeNotifications.value.individuals.size
+ /** The notifications that are promoted and ongoing. Sorted by priority order. */
+ val promotedOngoingNotifications: Flow<List<ActiveNotificationModel>> =
+ if (StatusBarNotifChips.isEnabled) {
+ // TODO(b/364653005): Filter all the notifications down to just the promoted ones.
+ // TODO(b/364653005): [ongoingCallNotification] should be incorporated into this flow
+ // instead of being separate.
+ topLevelRepresentativeNotifications
+ } else {
+ flowOf(emptyList())
+ }
+
/**
* The priority ongoing call notification, or null if there is no ongoing call.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
index 695e088b5f8b..fbec6406e9d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel
import android.content.Context
import android.icu.text.MessageFormat
-import com.android.app.tracing.coroutines.flow.flowOn
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
import com.android.systemui.modes.shared.ModesUi
@@ -40,6 +39,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 560028cb5640..d538f52fd637 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -449,7 +449,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
// We need to reset the View state, even if the animation was cancelled
- onAppearAnimationFinished(isAppearing);
+ onAppearAnimationFinished(isAppearing, /* cancelled = */ !mRunWithoutInterruptions);
if (mRunWithoutInterruptions) {
InteractionJankMonitor.getInstance().end(getCujType(isAppearing));
@@ -463,6 +463,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
if (onStartedRunnable != null) {
onStartedRunnable.run();
}
+ onAppearAnimationStarted(isAppearing);
mRunWithoutInterruptions = true;
Configuration.Builder builder = Configuration.Builder
.withView(getCujType(isAppearing), ActivatableNotificationView.this);
@@ -486,6 +487,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
frameTimeNanos -> {
if (mAppearAnimator == cachedAnimator) {
mAppearAnimator.start();
+ } else {
+ onAppearAnimationSkipped(isAppearing);
}
}, delay);
}
@@ -502,7 +505,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
}
- protected void onAppearAnimationFinished(boolean wasAppearing) {
+ protected void onAppearAnimationStarted(boolean isAppear) {
+ }
+
+ protected void onAppearAnimationSkipped(boolean isAppear) {
+ }
+
+ protected void onAppearAnimationFinished(boolean wasAppearing, boolean cancelled) {
}
private void cancelAppearAnimation() {
@@ -830,4 +839,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
});
}
}
+
+ protected void dumpAppearAnimationProperties(IndentingPrintWriter pw, String[] args) {
+ pw.print("AppearAnimation: ");
+ pw.print("mDrawingAppearAnimation", mDrawingAppearAnimation);
+ pw.print("mAppearAnimationFraction", mAppearAnimationFraction);
+ pw.print("mIsHeadsUpAnimation", mIsHeadsUpAnimation);
+ pw.print("mTargetPoint", mTargetPoint);
+ pw.println();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 49153d1d8eef..38e66099022c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1835,6 +1835,25 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
void logSkipResetAllContentAlphas(
NotificationEntry entry
);
+
+ /** Called when we start an appear animation. */
+ void logStartAppearAnimation(NotificationEntry entry, boolean isAppear);
+
+ /** Called when we cancel the running appear animation. */
+ void logCancelAppearDrawing(NotificationEntry entry, boolean wasDrawing);
+
+ /** Called when the animator of the appear animation is started. */
+ void logAppearAnimationStarted(NotificationEntry entry, boolean isAppear);
+
+ /** Called when we prepared an appear animation, but the animator was never started. */
+ void logAppearAnimationSkipped(NotificationEntry entry, boolean isAppear);
+
+ /** Called when the animator of the appear animation is finished. */
+ void logAppearAnimationFinished(
+ NotificationEntry entry,
+ boolean isAppear,
+ boolean cancelled
+ );
}
/**
@@ -3165,6 +3184,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
+ public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear,
+ Runnable onFinishRunnable) {
+ mLogger.logStartAppearAnimation(getEntry(), /* isAppear = */ true);
+ super.performAddAnimation(delay, duration, isHeadsUpAppear, onFinishRunnable);
+ }
+
+ @Override
public long performRemoveAnimation(
long duration,
long delay,
@@ -3173,6 +3199,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
Runnable onStartedRunnable,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener, ClipSide clipSide) {
+ mLogger.logStartAppearAnimation(getEntry(), /* isAppear = */ false);
if (mMenuRow != null && mMenuRow.isMenuVisible()) {
Animator anim = getTranslateViewAnimator(0f, null /* listener */);
if (anim != null) {
@@ -3201,8 +3228,25 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
- protected void onAppearAnimationFinished(boolean wasAppearing) {
- super.onAppearAnimationFinished(wasAppearing);
+ protected void onAppearAnimationStarted(boolean isAppear) {
+ mLogger.logAppearAnimationStarted(getEntry(), /* isAppear = */ isAppear);
+ super.onAppearAnimationStarted(isAppear);
+ }
+
+ @Override
+ protected void onAppearAnimationSkipped(boolean isAppear) {
+ mLogger.logAppearAnimationSkipped(getEntry(), /* isAppear = */ isAppear);
+ super.onAppearAnimationSkipped(isAppear);
+ }
+
+ @Override
+ protected void onAppearAnimationFinished(boolean wasAppearing, boolean cancelled) {
+ mLogger.logAppearAnimationFinished(
+ /* entry = */ getEntry(),
+ /* isAppear = */ wasAppearing,
+ /* cancelled = */ cancelled
+ );
+ super.onAppearAnimationFinished(wasAppearing, cancelled);
if (wasAppearing) {
// During the animation the visible view might have changed, so let's make sure all
// alphas are reset
@@ -3218,6 +3262,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
+ public void cancelAppearDrawing() {
+ mLogger.logCancelAppearDrawing(getEntry(), isDrawingAppearAnimation());
+ super.cancelAppearDrawing();
+ }
+
+ @Override
public void resetAllContentAlphas() {
mLogger.logResetAllContentAlphas(getEntry());
mPrivateLayout.setAlpha(1f);
@@ -3883,6 +3933,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
dumpHeights(pw);
}
showingLayout.dump(pw, args);
+ dumpAppearAnimationProperties(pw, args);
dumpCustomOutline(pw, args);
dumpClipping(pw, args);
if (getViewState() != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index c31a2cb8908b..23a2facf4c5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -209,6 +209,32 @@ public class ExpandableNotificationRowController implements NotifViewController
) {
mLogBufferLogger.logSkipResetAllContentAlphas(entry);
}
+
+ @Override
+ public void logStartAppearAnimation(NotificationEntry entry, boolean isAppear) {
+ mLogBufferLogger.logStartAppearAnimation(entry, isAppear);
+ }
+
+ @Override
+ public void logCancelAppearDrawing(NotificationEntry entry, boolean wasDrawing) {
+ mLogBufferLogger.logCancelAppearDrawing(entry, wasDrawing);
+ }
+
+ @Override
+ public void logAppearAnimationStarted(NotificationEntry entry, boolean isAppear) {
+ mLogBufferLogger.logAppearAnimationStarted(entry, isAppear);
+ }
+
+ @Override
+ public void logAppearAnimationSkipped(NotificationEntry entry, boolean isAppear) {
+ mLogBufferLogger.logAppearAnimationSkipped(entry, isAppear);
+ }
+
+ @Override
+ public void logAppearAnimationFinished(NotificationEntry entry, boolean isAppear,
+ boolean cancelled) {
+ mLogBufferLogger.logAppearAnimationFinished(entry, isAppear, cancelled);
+ }
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
index b90aa107d617..71f9c07ba2d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.row
import android.widget.flags.Flags.notifLinearlayoutOptimized
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory
import com.android.systemui.statusbar.notification.shared.NotificationViewFlipperPausing
import javax.inject.Inject
import javax.inject.Provider
@@ -35,6 +36,7 @@ constructor(
bigPictureLayoutInflaterFactory: BigPictureLayoutInflaterFactory,
optimizedLinearLayoutFactory: NotificationOptimizedLinearLayoutFactory,
notificationViewFlipperFactory: Provider<NotificationViewFlipperFactory>,
+ notificationRowIconViewInflaterFactory: NotificationRowIconViewInflaterFactory,
) : NotifRemoteViewsFactoryContainer {
override val factories: Set<NotifRemoteViewsFactory> = buildSet {
add(precomputedTextViewFactory)
@@ -47,5 +49,8 @@ constructor(
if (NotificationViewFlipperPausing.isEnabled) {
add(notificationViewFlipperFactory.get())
}
+ if (android.app.Flags.notificationsRedesignAppIcons()) {
+ add(notificationRowIconViewInflaterFactory)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index e10fd8f3b3fb..41abac1d47f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -342,7 +342,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
* Cancel any pending content view frees from {@link #freeNotificationView} for the provided
* content views.
*
- * @param row top level notification row containing the content views
+ * @param row top level notification row containing the content views
* @param contentViews content views to cancel pending frees on
*/
private void cancelContentViewFrees(
@@ -478,6 +478,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder
notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_HEADS_UP));
setRemoteViewsInflaterFactory(result.newPublicView,
notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_PUBLIC));
+ if (android.app.Flags.notificationsRedesignAppIcons()) {
+ setRemoteViewsInflaterFactory(result.mNewGroupHeaderView,
+ notifLayoutInflaterFactoryProvider.provide(row, FLAG_GROUP_SUMMARY_HEADER));
+ setRemoteViewsInflaterFactory(result.mNewMinimizedGroupHeaderView,
+ notifLayoutInflaterFactoryProvider.provide(row,
+ FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER));
+ }
}
private static void setRemoteViewsInflaterFactory(RemoteViews remoteViews,
@@ -516,6 +523,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
logger.logAsyncTaskProgress(entry, "contracted view applied");
result.inflatedContentView = v;
}
+
@Override
public RemoteViews getRemoteView() {
return result.newContentView;
@@ -1406,6 +1414,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
@VisibleForTesting
abstract static class ApplyCallback {
public abstract void setResultView(View v);
+
public abstract RemoteViews getRemoteView();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
index b1e90329e01a..6e78f561cfc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
@@ -30,7 +30,7 @@ class NotificationRowLogger
@Inject
constructor(
@NotificationLog private val buffer: LogBuffer,
- @NotificationRenderLog private val notificationRenderBuffer: LogBuffer
+ @NotificationRenderLog private val notificationRenderBuffer: LogBuffer,
) {
fun logKeepInParentChildDetached(child: NotificationEntry, oldParent: NotificationEntry?) {
buffer.log(
@@ -40,7 +40,7 @@ constructor(
str1 = child.logKey
str2 = oldParent.logKey
},
- { "Detach child $str1 kept in parent $str2" }
+ { "Detach child $str1 kept in parent $str2" },
)
}
@@ -52,13 +52,13 @@ constructor(
str1 = child.logKey
str2 = newParent.logKey
},
- { "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" }
+ { "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" },
)
}
fun logRemoveTransientFromContainer(
childEntry: NotificationEntry,
- containerEntry: NotificationEntry
+ containerEntry: NotificationEntry,
) {
notificationRenderBuffer.log(
TAG,
@@ -67,25 +67,20 @@ constructor(
str1 = childEntry.logKey
str2 = containerEntry.logKey
},
- { "RemoveTransientRow from ChildrenContainer: childKey: $str1 -- containerKey: $str2" }
+ { "RemoveTransientRow from ChildrenContainer: childKey: $str1 -- containerKey: $str2" },
)
}
- fun logRemoveTransientFromNssl(
- childEntry: NotificationEntry,
- ) {
+ fun logRemoveTransientFromNssl(childEntry: NotificationEntry) {
notificationRenderBuffer.log(
TAG,
LogLevel.INFO,
{ str1 = childEntry.logKey },
- { "RemoveTransientRow from Nssl: childKey: $str1" }
+ { "RemoveTransientRow from Nssl: childKey: $str1" },
)
}
- fun logRemoveTransientFromViewGroup(
- childEntry: NotificationEntry,
- containerView: ViewGroup,
- ) {
+ fun logRemoveTransientFromViewGroup(childEntry: NotificationEntry, containerView: ViewGroup) {
notificationRenderBuffer.log(
TAG,
LogLevel.WARNING,
@@ -93,14 +88,14 @@ constructor(
str1 = childEntry.logKey
str2 = containerView.toString()
},
- { "RemoveTransientRow from other ViewGroup: childKey: $str1 -- ViewGroup: $str2" }
+ { "RemoveTransientRow from other ViewGroup: childKey: $str1 -- ViewGroup: $str2" },
)
}
fun logAddTransientRow(
childEntry: NotificationEntry,
containerEntry: NotificationEntry,
- index: Int
+ index: Int,
) {
notificationRenderBuffer.log(
TAG,
@@ -110,14 +105,11 @@ constructor(
str2 = containerEntry.logKey
int1 = index
},
- { "addTransientRow to row: childKey: $str1 -- containerKey: $str2 -- index: $int1" }
+ { "addTransientRow to row: childKey: $str1 -- containerKey: $str2 -- index: $int1" },
)
}
- fun logRemoveTransientRow(
- childEntry: NotificationEntry,
- containerEntry: NotificationEntry,
- ) {
+ fun logRemoveTransientRow(childEntry: NotificationEntry, containerEntry: NotificationEntry) {
notificationRenderBuffer.log(
TAG,
LogLevel.ERROR,
@@ -125,7 +117,7 @@ constructor(
str1 = childEntry.logKey
str2 = containerEntry.logKey
},
- { "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" }
+ { "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" },
)
}
@@ -134,7 +126,7 @@ constructor(
TAG,
LogLevel.INFO,
{ str1 = entry.logKey },
- { "resetAllContentAlphas: $str1" }
+ { "resetAllContentAlphas: $str1" },
)
}
@@ -143,7 +135,72 @@ constructor(
TAG,
LogLevel.INFO,
{ str1 = entry.logKey },
- { "Skip resetAllContentAlphas: $str1" }
+ { "Skip resetAllContentAlphas: $str1" },
+ )
+ }
+
+ fun logStartAppearAnimation(entry: NotificationEntry, isAppear: Boolean) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = entry.logKey
+ bool1 = isAppear
+ },
+ { "startAppearAnimation childKey: $str1 isAppear:$bool1" },
+ )
+ }
+
+ fun logCancelAppearDrawing(entry: NotificationEntry, wasDrawing: Boolean) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.WARNING,
+ {
+ str1 = entry.logKey
+ bool1 = wasDrawing
+ },
+ { "cancelAppearDrawing childKey: $str1 wasDrawing:$bool1" },
+ )
+ }
+
+ fun logAppearAnimationStarted(entry: NotificationEntry, isAppear: Boolean) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = entry.logKey
+ bool1 = isAppear
+ },
+ { "onAppearAnimationStarted childKey: $str1 isAppear:$bool1" },
+ )
+ }
+
+ fun logAppearAnimationSkipped(entry: NotificationEntry, isAppear: Boolean) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.WARNING,
+ {
+ str1 = entry.logKey
+ bool1 = isAppear
+ },
+ { "Skipped an appear animation childKey: $str1 isAppear:$bool1" },
+ )
+ }
+
+ fun logAppearAnimationFinished(
+ entry: NotificationEntry,
+ isAppear: Boolean,
+ cancelled: Boolean,
+ ) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = entry.logKey
+ bool1 = isAppear
+ bool2 = cancelled
+ },
+ { "onAppearAnimationFinished childKey: $str1 isAppear:$bool1 cancelled:$bool2" },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
index 84f2f6670839..4feb78a46d60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.notification.row;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.notification.row.icon.AppIconProviderModule;
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProviderModule;
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
import dagger.Binds;
@@ -28,7 +30,7 @@ import javax.inject.Provider;
/**
* Dagger Module containing notification row and view inflation implementations.
*/
-@Module
+@Module(includes = {AppIconProviderModule.class, NotificationIconStyleProviderModule.class})
public abstract class NotificationRowModule {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
new file mode 100644
index 000000000000..24b5cf1aa33b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.systemui.statusbar.notification.row.icon
+
+import android.app.ActivityManager
+import android.app.Flags
+import android.content.Context
+import android.content.pm.PackageManager.NameNotFoundException
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.util.Log
+import com.android.internal.R
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Module
+import dagger.Provides
+import javax.inject.Inject
+import javax.inject.Provider
+
+/** A provider used to cache and fetch app icons used by notifications. */
+interface AppIconProvider {
+ @Throws(NameNotFoundException::class)
+ fun getOrFetchAppIcon(packageName: String, context: Context): Drawable
+}
+
+@SysUISingleton
+class AppIconProviderImpl @Inject constructor(private val sysuiContext: Context) : AppIconProvider {
+ private val iconFactory: BaseIconFactory
+ get() {
+ val isLowRam = ActivityManager.isLowRamDeviceStatic()
+ val res = sysuiContext.resources
+ val iconSize: Int =
+ res.getDimensionPixelSize(
+ if (isLowRam) R.dimen.notification_small_icon_size_low_ram
+ else R.dimen.notification_small_icon_size
+ )
+ return BaseIconFactory(sysuiContext, res.configuration.densityDpi, iconSize)
+ }
+
+ override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable {
+ val icon = context.packageManager.getApplicationIcon(packageName)
+ return BitmapDrawable(
+ context.resources,
+ iconFactory.createScaledBitmap(icon, BaseIconFactory.MODE_HARDWARE),
+ )
+ }
+}
+
+class NoOpIconProvider : AppIconProvider {
+ companion object {
+ const val TAG = "NoOpIconProvider"
+ }
+
+ override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable {
+ Log.wtf(TAG, "NoOpIconProvider should not be used anywhere.")
+ return ColorDrawable(Color.WHITE)
+ }
+}
+
+@Module
+class AppIconProviderModule {
+ @Provides
+ @SysUISingleton
+ fun provideImpl(realImpl: Provider<AppIconProviderImpl>): AppIconProvider =
+ if (Flags.notificationsRedesignAppIcons()) {
+ realImpl.get()
+ } else {
+ NoOpIconProvider()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
new file mode 100644
index 000000000000..165c1a7803a9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.systemui.statusbar.notification.row.icon
+
+import android.annotation.WorkerThread
+import android.app.Flags
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.service.notification.StatusBarNotification
+import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Module
+import dagger.Provides
+import javax.inject.Inject
+import javax.inject.Provider
+
+/**
+ * A provider used to cache and fetch information about which icon should be displayed by
+ * notifications.
+ */
+interface NotificationIconStyleProvider {
+ @WorkerThread
+ fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean
+}
+
+@SysUISingleton
+class NotificationIconStyleProviderImpl @Inject constructor() : NotificationIconStyleProvider {
+ override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean {
+ val packageContext = notification.getPackageContext(context)
+ return !belongsToHeadlessSystemApp(packageContext)
+ }
+
+ @WorkerThread
+ private fun belongsToHeadlessSystemApp(context: Context): Boolean {
+ val info = context.applicationInfo
+ if (info != null) {
+ if ((info.flags and ApplicationInfo.FLAG_SYSTEM) == 0) {
+ // It's not a system app at all.
+ return false
+ } else {
+ // If there's no launch intent, it's probably a headless app.
+ val pm = context.packageManager
+ return (pm.getLaunchIntentForPackage(info.packageName) == null)
+ }
+ } else {
+ // If for some reason we don't have the app info, we don't know; best assume it's
+ // not a system app.
+ return false
+ }
+ }
+}
+
+class NoOpIconStyleProvider : NotificationIconStyleProvider {
+ companion object {
+ const val TAG = "NoOpIconStyleProvider"
+ }
+
+ override fun shouldShowAppIcon(notification: StatusBarNotification, context: Context): Boolean {
+ Log.wtf(TAG, "NoOpIconStyleProvider should not be used anywhere.")
+ return true
+ }
+}
+
+@Module
+class NotificationIconStyleProviderModule {
+ @Provides
+ @SysUISingleton
+ fun provideImpl(
+ realImpl: Provider<NotificationIconStyleProviderImpl>
+ ): NotificationIconStyleProvider =
+ if (Flags.notificationsRedesignAppIcons()) {
+ realImpl.get()
+ } else {
+ NoOpIconStyleProvider()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
new file mode 100644
index 000000000000..79defd255de0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.systemui.statusbar.notification.row.icon
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.view.View
+import com.android.internal.widget.NotificationRowIconView
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.NotifRemoteViewsFactory
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder
+import javax.inject.Inject
+
+/**
+ * A factory which owns the construction of any NotificationRowIconView inside of Notifications in
+ * SystemUI. This allows overriding the small icon with the app icon in notifications.
+ */
+class NotificationRowIconViewInflaterFactory
+@Inject
+constructor(
+ private val appIconProvider: AppIconProvider,
+ private val iconStyleProvider: NotificationIconStyleProvider,
+) : NotifRemoteViewsFactory {
+ override fun instantiate(
+ row: ExpandableNotificationRow,
+ @NotificationRowContentBinder.InflationFlag layoutType: Int,
+ parent: View?,
+ name: String,
+ context: Context,
+ attrs: AttributeSet,
+ ): View? {
+ return when (name) {
+ NotificationRowIconView::class.java.name ->
+ NotificationRowIconView(context, attrs).also { view ->
+ val sbn = row.entry.sbn
+ view.setIconProvider(
+ object : NotificationRowIconView.NotificationIconProvider {
+ override fun shouldShowAppIcon(): Boolean {
+ return iconStyleProvider.shouldShowAppIcon(row.entry.sbn, context)
+ }
+
+ override fun getAppIcon(): Drawable {
+ return appIconProvider.getOrFetchAppIcon(sbn.packageName, context)
+ }
+ }
+ )
+ }
+ else -> null
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 00c5c40fc8ac..379a67ed3942 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -2122,9 +2122,6 @@ public class NotificationStackScrollLayoutController implements Dumpable {
boolean hunWantsIt = false;
if (shouldHeadsUpHandleTouch()) {
hunWantsIt = mHeadsUpTouchHelper.onInterceptTouchEvent(ev);
- if (hunWantsIt) {
- mView.startDraggingOnHun();
- }
}
boolean swipeWantsIt = false;
if (mLongPressedView == null && !mView.isBeingDragged()
@@ -2210,6 +2207,9 @@ public class NotificationStackScrollLayoutController implements Dumpable {
boolean hunWantsIt = false;
if (shouldHeadsUpHandleTouch()) {
hunWantsIt = mHeadsUpTouchHelper.onTouchEvent(ev);
+ if (hunWantsIt) {
+ mView.startDraggingOnHun();
+ }
}
// Check if we need to clear any snooze leavebehinds
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 87d70ba12012..fb42ee7908b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -17,7 +17,8 @@
package com.android.systemui.statusbar.notification.stack.ui.viewbinder
import android.util.Log
-import com.android.app.tracing.coroutines.flow.filter
+import com.android.app.tracing.coroutines.flow.collectTraced
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.view.onLayoutChanged
import com.android.systemui.dagger.SysUISingleton
@@ -37,7 +38,6 @@ import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.launch
/** Binds the [NotificationScrollView]. */
@SysUISingleton
@@ -79,39 +79,39 @@ constructor(
launch {
viewModel
.shadeScrimShape(cornerRadius = scrimRadius, viewLeftOffset = viewLeftOffset)
- .collect { view.setScrimClippingShape(it) }
+ .collectTraced { view.setScrimClippingShape(it) }
}
- launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
- launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
+ launch { viewModel.maxAlpha.collectTraced { view.setMaxAlpha(it) } }
+ launch { viewModel.scrolledToTop.collectTraced { view.setScrolledToTop(it) } }
launch {
- viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) }
+ viewModel.expandFraction.collectTraced { view.setExpandFraction(it.coerceIn(0f, 1f)) }
}
- launch { viewModel.qsExpandFraction.collect { view.setQsExpandFraction(it) } }
+ launch { viewModel.qsExpandFraction.collectTraced { view.setQsExpandFraction(it) } }
launch {
- viewModel.isShowingStackOnLockscreen.collect {
+ viewModel.isShowingStackOnLockscreen.collectTraced {
view.setShowingStackOnLockscreen(it)
}
}
launch {
- viewModel.alphaForLockscreenFadeIn.collect { view.setAlphaForLockscreenFadeIn(it) }
+ viewModel.alphaForLockscreenFadeIn.collectTraced { view.setAlphaForLockscreenFadeIn(it) }
}
- launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
- launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
+ launch { viewModel.isScrollable.collectTraced { view.setScrollingEnabled(it) } }
+ launch { viewModel.isDozing.collectTraced { isDozing -> view.setDozing(isDozing) } }
launch {
- viewModel.isPulsing.collect { isPulsing ->
+ viewModel.isPulsing.collectTraced { isPulsing ->
view.setPulsing(isPulsing, viewModel.shouldAnimatePulse.value)
}
}
launch {
viewModel.shouldResetStackTop
.filter { it }
- .collect { view.setStackTop(-(view.getHeadsUpInset().toFloat())) }
+ .collectTraced { view.setStackTop(-(view.getHeadsUpInset().toFloat())) }
}
launch {
- viewModel.shouldCloseGuts.filter { it }.collect { view.closeGutsOnSceneTouch() }
+ viewModel.shouldCloseGuts.filter { it }.collectTraced { view.closeGutsOnSceneTouch() }
}
- launch { viewModel.suppressHeightUpdates.collect { view.suppressHeightUpdates(it) } }
+ launch { viewModel.suppressHeightUpdates.collectTraced { view.suppressHeightUpdates(it) } }
launchAndDispose {
view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index f39af18afcea..878ae91391e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -20,6 +20,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.flow.flowName
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.dagger.SysUISingleton
@@ -158,6 +159,7 @@ constructor(
) { shadeExpansion, qsExpansion ->
shadeExpansion || qsExpansion
}
+ .flowName("isAnyExpanded")
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -175,6 +177,7 @@ constructor(
isAnyExpanded ->
isShadeLocked && isAnyExpanded
}
+ .flowName("isShadeLocked")
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -227,6 +230,7 @@ constructor(
),
keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
)
+ .flowName("isOnLockscreen")
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -239,6 +243,7 @@ constructor(
combine(isOnLockscreen, isAnyExpanded) { isKeyguard, isAnyExpanded ->
isKeyguard && !isAnyExpanded
}
+ .flowName("isOnLockscreenWithoutShade")
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -274,6 +279,7 @@ constructor(
combine(isOnGlanceableHub, isAnyExpanded) { isGlanceableHub, isAnyExpanded ->
isGlanceableHub && !isAnyExpanded
}
+ .flowName("isOnGlanceableHubWithoutShade")
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -288,6 +294,7 @@ constructor(
isAnyExpanded ->
isDreaming && !isAnyExpanded
}
+ .flowName("isDreamingWithoutShade")
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -345,6 +352,7 @@ constructor(
}
}
}
+ .flowName("shadeCollapseFadeIn")
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
@@ -382,6 +390,7 @@ constructor(
bounds.copy(top = top, isAnimated = animate)
}
}
+ .flowName("bounds")
.stateIn(
scope = applicationScope,
started = SharingStarted.Lazily,
@@ -495,6 +504,7 @@ constructor(
// flatMapLatest below, the last value gets emitted, to avoid the randomness of `merge`.
val alphaForTransitionsAndShade =
merge(alphaForTransitions(viewState), alphaForShadeAndQsExpansion)
+ .flowName("alphaForTransitionsAndShade")
.stateIn(
// Use view-level scope instead of ApplicationScope, to prevent collection that
// never stops
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index cb4454d88b2d..3d7cd9c9fbcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -128,7 +128,6 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.emergency.EmergencyGesture;
import com.android.systemui.emergency.EmergencyGestureModule.EmergencyGestureIntentFactory;
import com.android.systemui.flags.FeatureFlags;
@@ -2825,23 +2824,13 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mScrimController.setExpansionAffectsAlpha(!unlocking);
if (mAlternateBouncerInteractor.isVisibleState()) {
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
- && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
- || mTransitionToFullShadeProgress > 0f)) {
- // Assume scrim state for shade is already correct and do nothing
- } else {
- // Safeguard which prevents the scrim from being stuck in the wrong state
- mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
- }
+ if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
+ && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+ || mTransitionToFullShadeProgress > 0f)) {
+ // Assume scrim state for shade is already correct and do nothing
} else {
- if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
- && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
- || mTransitionToFullShadeProgress > 0f)) {
- mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
- } else {
- mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED);
- }
+ // Safeguard which prevents the scrim from being stuck in the wrong state
+ mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
}
// This will cancel the keyguardFadingAway animation if it is running. We need to do
// this as otherwise it can remain pending and leave keyguard in a weird state.
@@ -3168,12 +3157,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
public void onDozeAmountChanged(float linear, float eased) {
if (!lightRevealMigration()
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- // If wakeAndUnlocking, this is handled in AuthRippleInteractor
- if (!mBiometricUnlockController.isWakeAndUnlock()) {
- mLightRevealScrim.setRevealAmount(1f - linear);
- }
- } else {
+ // If wakeAndUnlocking, this is handled in AuthRippleInteractor
+ if (!mBiometricUnlockController.isWakeAndUnlock()) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 316e1f13bc2b..a34ac2e11c2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -22,7 +22,7 @@ import android.content.res.Resources
import android.hardware.biometrics.BiometricSourceType
import android.provider.Settings
import com.android.app.tracing.ListenersTracing.forEachTraced
-import com.android.app.tracing.coroutines.launch
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 069c6241491c..40e52935daf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -1715,26 +1715,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
updateScrims();
}
- public void setHasBackdrop(boolean hasBackdrop) {
- for (ScrimState state : ScrimState.values()) {
- state.setHasBackdrop(hasBackdrop);
- }
-
- // Backdrop event may arrive after state was already applied,
- // in this case, back-scrim needs to be re-evaluated
- if (mState == ScrimState.AOD || mState == ScrimState.PULSING) {
- float newBehindAlpha = mState.getBehindAlpha();
- if (isNaN(newBehindAlpha)) {
- throw new IllegalStateException("Scrim opacity is NaN for state: " + mState
- + ", back: " + mBehindAlpha);
- }
- if (mBehindAlpha != newBehindAlpha) {
- mBehindAlpha = newBehindAlpha;
- updateScrims();
- }
- }
- }
-
private void setKeyguardFadingAway(boolean fadingAway, long duration) {
for (ScrimState state : ScrimState.values()) {
state.setKeyguardFadingAway(fadingAway, duration);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index fbba3dc107f1..a0ab6120f372 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -210,7 +210,7 @@ public enum ScrimState {
@Override
public float getMaxLightRevealScrimAlpha() {
- return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
+ return mWallpaperSupportsAmbientMode ? 0f : 1f;
}
@Override
@@ -369,7 +369,6 @@ public enum ScrimState {
DockManager mDockManager;
boolean mDisplayRequiresBlanking;
boolean mWallpaperSupportsAmbientMode;
- boolean mHasBackdrop;
boolean mLaunchingAffordanceWithPreview;
boolean mOccludeAnimationPlaying;
boolean mWakeLockScreenSensorActive;
@@ -489,10 +488,6 @@ public enum ScrimState {
return false;
}
- public void setHasBackdrop(boolean hasBackdrop) {
- mHasBackdrop = hasBackdrop;
- }
-
public void setWakeLockScreenSensorActive(boolean active) {
mWakeLockScreenSensorActive = active;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 613efaa148f5..c6f6bd90fce6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -34,10 +34,10 @@ import com.android.systemui.Dumpable
import com.android.systemui.StatusBarInsetsCommand
import com.android.systemui.SysUICutoutInformation
import com.android.systemui.SysUICutoutProvider
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl.CacheKey
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
@@ -47,9 +47,11 @@ import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
import com.android.systemui.util.leak.RotationUtils.Rotation
import com.android.systemui.util.leak.RotationUtils.getExactRotation
import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
import java.io.PrintWriter
import java.lang.Math.max
-import javax.inject.Inject
/**
* Encapsulates logic that can solve for the left/right insets required for the status bar contents.
@@ -64,19 +66,87 @@ import javax.inject.Inject
*
* NOTE: This class is not threadsafe
*/
-@SysUISingleton
-class StatusBarContentInsetsProvider
-@Inject
+interface StatusBarContentInsetsProvider :
+ CallbackController<StatusBarContentInsetsChangedListener> {
+
+ /**
+ * Some views may need to care about whether or not the current top display cutout is located in
+ * the corner rather than somewhere in the center. In the case of a corner cutout, the status
+ * bar area is contiguous.
+ */
+ fun currentRotationHasCornerCutout(): Boolean
+
+ /**
+ * Calculates the maximum bounding rectangle for the privacy chip animation + ongoing privacy
+ * dot in the coordinates relative to the given rotation.
+ *
+ * @param rotation the rotation for which the bounds are required. This is an absolute value
+ * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from
+ * which this method is called)
+ */
+ fun getBoundingRectForPrivacyChipForRotation(
+ @Rotation rotation: Int,
+ displayCutout: DisplayCutout?,
+ ): Rect
+
+ /**
+ * Calculate the distance from the left, right and top edges of the screen to the status bar
+ * content area. This differs from the content area rects in that these values can be used
+ * directly as padding.
+ *
+ * @param rotation the target rotation for which to calculate insets
+ */
+ fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Insets
+
+ /**
+ * Calculate the insets for the status bar content in the device's current rotation
+ *
+ * @see getStatusBarContentAreaForRotation
+ */
+ fun getStatusBarContentInsetsForCurrentRotation(): Insets
+
+ /**
+ * Calculates the area of the status bar contents invariant of the current device rotation, in
+ * the target rotation's coordinates
+ *
+ * @param rotation the rotation for which the bounds are required. This is an absolute value
+ * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from
+ * which this method is called)
+ */
+ fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect
+
+ /** Get the status bar content area for the given rotation, in absolute bounds */
+ fun getStatusBarContentAreaForCurrentRotation(): Rect
+
+ fun getStatusBarPaddingTop(@Rotation rotation: Int? = null): Int
+
+ interface Factory {
+ fun create(
+ context: Context,
+ configurationController: ConfigurationController,
+ sysUICutoutProvider: SysUICutoutProvider,
+ ): StatusBarContentInsetsProvider
+ }
+}
+
+class StatusBarContentInsetsProviderImpl
+@AssistedInject
constructor(
- val context: Context,
- val configurationController: ConfigurationController,
+ @Assisted val context: Context,
+ @Assisted val configurationController: ConfigurationController,
val dumpManager: DumpManager,
val commandRegistry: CommandRegistry,
- val sysUICutoutProvider: SysUICutoutProvider,
-) :
- CallbackController<StatusBarContentInsetsChangedListener>,
- ConfigurationController.ConfigurationListener,
- Dumpable {
+ @Assisted val sysUICutoutProvider: SysUICutoutProvider,
+) : StatusBarContentInsetsProvider, ConfigurationController.ConfigurationListener, Dumpable {
+
+ @AssistedFactory
+ interface Factory : StatusBarContentInsetsProvider.Factory {
+ override fun create(
+ context: Context,
+ configurationController: ConfigurationController,
+ sysUICutoutProvider: SysUICutoutProvider,
+ ): StatusBarContentInsetsProviderImpl
+ }
// Limit cache size as potentially we may connect large number of displays
// (e.g. network displays)
@@ -95,7 +165,7 @@ constructor(
object : StatusBarInsetsCommand.Callback {
override fun onExecute(
command: StatusBarInsetsCommand,
- printWriter: PrintWriter
+ printWriter: PrintWriter,
) {
executeCommand(command, printWriter)
}
@@ -133,12 +203,7 @@ constructor(
listeners.forEach { it.onStatusBarContentInsetsChanged() }
}
- /**
- * Some views may need to care about whether or not the current top display cutout is located in
- * the corner rather than somewhere in the center. In the case of a corner cutout, the status
- * bar area is contiguous.
- */
- fun currentRotationHasCornerCutout(): Boolean {
+ override fun currentRotationHasCornerCutout(): Boolean {
val cutout = checkNotNull(context.display).cutout ?: return false
val topBounds = cutout.boundingRectTop
@@ -148,17 +213,9 @@ constructor(
return topBounds.left <= 0 || topBounds.right >= point.x
}
- /**
- * Calculates the maximum bounding rectangle for the privacy chip animation + ongoing privacy
- * dot in the coordinates relative to the given rotation.
- *
- * @param rotation the rotation for which the bounds are required. This is an absolute value
- * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from
- * which this method is called)
- */
- fun getBoundingRectForPrivacyChipForRotation(
+ override fun getBoundingRectForPrivacyChipForRotation(
@Rotation rotation: Int,
- displayCutout: DisplayCutout?
+ displayCutout: DisplayCutout?,
): Rect {
val key = getCacheKey(rotation, displayCutout)
var insets = insetsCache[key]
@@ -176,14 +233,7 @@ constructor(
return getPrivacyChipBoundingRectForInsets(insets, dotWidth, chipWidth, isRtl)
}
- /**
- * Calculate the distance from the left, right and top edges of the screen to the status bar
- * content area. This differs from the content area rects in that these values can be used
- * directly as padding.
- *
- * @param rotation the target rotation for which to calculate insets
- */
- fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Insets =
+ override fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Insets =
traceSection(tag = "StatusBarContentInsetsProvider.getStatusBarContentInsetsForRotation") {
val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplayAndRotation()
val displayCutout = sysUICutout?.cutout
@@ -202,31 +252,17 @@ constructor(
rotation,
sysUICutout,
getResourcesForRotation(rotation, context),
- key
+ key,
)
Insets.of(area.left, area.top, /* right= */ width - area.right, /* bottom= */ 0)
}
- /**
- * Calculate the insets for the status bar content in the device's current rotation
- *
- * @see getStatusBarContentAreaForRotation
- */
- fun getStatusBarContentInsetsForCurrentRotation(): Insets {
+ override fun getStatusBarContentInsetsForCurrentRotation(): Insets {
return getStatusBarContentInsetsForRotation(getExactRotation(context))
}
- /**
- * Calculates the area of the status bar contents invariant of the current device rotation, in
- * the target rotation's coordinates
- *
- * @param rotation the rotation for which the bounds are required. This is an absolute value
- * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from
- * which this method is called)
- */
- @JvmOverloads
- fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect {
+ override fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect {
val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplayAndRotation()
val displayCutout = sysUICutout?.cutout
val key = getCacheKey(rotation, displayCutout)
@@ -235,12 +271,11 @@ constructor(
rotation,
sysUICutout,
getResourcesForRotation(rotation, context),
- key
+ key,
)
}
- /** Get the status bar content area for the given rotation, in absolute bounds */
- fun getStatusBarContentAreaForCurrentRotation(): Rect {
+ override fun getStatusBarContentAreaForCurrentRotation(): Rect {
val rotation = getExactRotation(context)
return getStatusBarContentAreaForRotation(rotation)
}
@@ -249,7 +284,7 @@ constructor(
@Rotation targetRotation: Int,
sysUICutout: SysUICutoutInformation?,
rotatedResources: Resources,
- key: CacheKey
+ key: CacheKey,
): Rect {
return getCalculatedAreaForRotation(sysUICutout, targetRotation, rotatedResources).also {
insetsCache.put(key, it)
@@ -259,7 +294,7 @@ constructor(
private fun getCalculatedAreaForRotation(
sysUICutout: SysUICutoutInformation?,
@Rotation targetRotation: Int,
- rotatedResources: Resources
+ rotatedResources: Resources,
): Rect {
val currentRotation = getExactRotation(context)
@@ -299,7 +334,7 @@ constructor(
configurationController.isLayoutRtl,
dotWidth,
bottomAlignedMargin,
- statusBarContentHeight
+ statusBarContentHeight,
)
}
@@ -349,7 +384,7 @@ constructor(
return resources.getDimensionPixelSize(dimenRes)
}
- fun getStatusBarPaddingTop(@Rotation rotation: Int? = null): Int {
+ override fun getStatusBarPaddingTop(@Rotation rotation: Int?): Int {
val res = rotation?.let { it -> getResourcesForRotation(it, context) } ?: context.resources
return res.getDimensionPixelSize(R.dimen.status_bar_padding_top)
}
@@ -364,13 +399,13 @@ constructor(
CacheKey(
rotation = rotation,
displaySize = Rect(context.resources.configuration.windowConfiguration.maxBounds),
- displayCutout = displayCutout
+ displayCutout = displayCutout,
)
private data class CacheKey(
@Rotation val rotation: Int,
val displaySize: Rect,
- val displayCutout: DisplayCutout?
+ val displayCutout: DisplayCutout?,
)
}
@@ -395,21 +430,21 @@ fun getPrivacyChipBoundingRectForInsets(
contentRect: Rect,
dotWidth: Int,
chipWidth: Int,
- isRtl: Boolean
+ isRtl: Boolean,
): Rect {
return if (isRtl) {
Rect(
contentRect.left - dotWidth,
contentRect.top,
contentRect.left + chipWidth,
- contentRect.bottom
+ contentRect.bottom,
)
} else {
Rect(
contentRect.right - chipWidth,
contentRect.top,
contentRect.right + dotWidth,
- contentRect.bottom
+ contentRect.bottom,
)
}
}
@@ -443,7 +478,7 @@ fun calculateInsetsForRotationWithRotatedResources(
isRtl: Boolean,
dotWidth: Int,
bottomAlignedMargin: Int,
- statusBarContentHeight: Int
+ statusBarContentHeight: Int,
): Rect {
/*
TODO: Check if this is ever used for devices with no rounded corners
@@ -467,7 +502,7 @@ fun calculateInsetsForRotationWithRotatedResources(
targetRotation,
currentRotation,
bottomAlignedMargin,
- statusBarContentHeight
+ statusBarContentHeight,
)
}
@@ -503,7 +538,7 @@ private fun getStatusBarContentBounds(
@Rotation targetRotation: Int,
@Rotation currentRotation: Int,
bottomAlignedMargin: Int,
- statusBarContentHeight: Int
+ statusBarContentHeight: Int,
): Rect {
val insetTop = getInsetTop(bottomAlignedMargin, statusBarContentHeight, sbHeight)
@@ -597,7 +632,7 @@ private val DisplayCutout.boundingRectsLeftRightTop
private fun getInsetTop(
bottomAlignedMargin: Int,
statusBarContentHeight: Int,
- statusBarHeight: Int
+ statusBarHeight: Int,
): Int {
val bottomAlignmentEnabled = bottomAlignedMargin >= 0
if (!bottomAlignmentEnabled) {
@@ -610,7 +645,7 @@ private fun getInsetTop(
private fun sbRect(
@Rotation relativeRotation: Int,
sbHeight: Int,
- displaySize: Pair<Int, Int>
+ displaySize: Pair<Int, Int>,
): Rect {
val w = displaySize.first
val h = displaySize.second
@@ -626,7 +661,7 @@ private fun shareShortEdge(
sbRect: Rect,
cutoutRect: Rect,
currentWidth: Int,
- currentHeight: Int
+ currentHeight: Int,
): Boolean {
if (currentWidth < currentHeight) {
// Check top/bottom edges by extending the width of the display cutout rect and checking
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 17bd53869ee5..74c6e72d3400 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -47,7 +47,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.KeyguardMessageAreaController;
import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -68,7 +67,6 @@ import com.android.systemui.bouncer.util.BouncerTestUtilsKt;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -77,9 +75,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInte
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.DismissAction;
-import com.android.systemui.keyguard.shared.model.Edge;
import com.android.systemui.keyguard.shared.model.KeyguardDone;
-import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.navigationbar.TaskbarDelegate;
@@ -165,7 +161,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private final DreamOverlayStateController mDreamOverlayStateController;
@Nullable
private final FoldAodAnimationController mFoldAodAnimationController;
- KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController;
private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -463,11 +458,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
onPanelExpansionChanged(currentState);
}
mNotificationContainer = notificationContainer;
- if (!DeviceEntryUdfpsRefactor.isEnabled()) {
- mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create(
- centralSurfaces.getKeyguardMessageArea());
- }
-
mCentralSurfacesRegistered = true;
registerListeners();
@@ -518,24 +508,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mListenForCanShowAlternateBouncer.cancel(null);
}
mListenForCanShowAlternateBouncer = null;
- if (!DeviceEntryUdfpsRefactor.isEnabled()) {
- mListenForAlternateBouncerTransitionSteps = mJavaAdapter.alwaysCollectFlow(
- mKeyguardTransitionInteractor
- .transition(Edge.create(KeyguardState.ALTERNATE_BOUNCER)),
- this::consumeFromAlternateBouncerTransitionSteps
- );
-
- mListenForKeyguardAuthenticatedBiometricsHandled = mJavaAdapter.alwaysCollectFlow(
- mPrimaryBouncerInteractor.getKeyguardAuthenticatedBiometricsHandled(),
- this::consumeKeyguardAuthenticatedBiometricsHandled
- );
- } else {
- // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot.
- mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow(
- mAlternateBouncerInteractor.getCanShowAlternateBouncer(),
- this::consumeCanShowAlternateBouncer
- );
- }
+ // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot.
+ mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow(
+ mAlternateBouncerInteractor.getCanShowAlternateBouncer(),
+ this::consumeCanShowAlternateBouncer
+ );
if (KeyguardWmStateRefactor.isEnabled()) {
// Show the keyguard views whenever we've told WM that the lockscreen is visible.
@@ -792,21 +769,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
return;
}
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) {
- Log.d(TAG, "showBouncer:alternateBouncer.forceShow()");
- mAlternateBouncerInteractor.forceShow();
- updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState());
- } else {
- showPrimaryBouncer(scrimmed);
- }
- return;
- }
-
- if (!mAlternateBouncerInteractor.show()) {
- showPrimaryBouncer(scrimmed);
- } else {
+ if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) {
+ Log.d(TAG, "showBouncer:alternateBouncer.forceShow()");
+ mAlternateBouncerInteractor.forceShow();
updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState());
+ } else {
+ showPrimaryBouncer(scrimmed);
}
}
@@ -921,13 +889,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mKeyguardGoneCancelAction = null;
}
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- Log.d(TAG, "dismissWithAction:alternateBouncer.forceShow()");
- mAlternateBouncerInteractor.forceShow();
- updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState());
- } else {
- updateAlternateBouncerShowing(mAlternateBouncerInteractor.show());
- }
+ Log.d(TAG, "dismissWithAction:alternateBouncer.forceShow()");
+ mAlternateBouncerInteractor.forceShow();
+ updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState());
setKeyguardMessage(message, null, null);
return;
}
@@ -1033,11 +997,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
final boolean isShowingAlternateBouncer = mAlternateBouncerInteractor.isVisibleState();
- if (mKeyguardMessageAreaController != null) {
- DeviceEntryUdfpsRefactor.assertInLegacyMode();
- mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer);
- mKeyguardMessageAreaController.setMessage("");
- }
if (!SceneContainerFlag.isEnabled()) {
mKeyguardUpdateManager.setAlternateBouncerShowing(isShowingAlternateBouncer);
}
@@ -1646,12 +1605,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
/** Display security message to relevant KeyguardMessageArea. */
public void setKeyguardMessage(String message, ColorStateList colorState,
BiometricSourceType biometricSourceType) {
- if (mAlternateBouncerInteractor.isVisibleState()) {
- if (mKeyguardMessageAreaController != null) {
- DeviceEntryUdfpsRefactor.assertInLegacyMode();
- mKeyguardMessageAreaController.setMessage(message, biometricSourceType);
- }
- } else {
+ if (!mAlternateBouncerInteractor.isVisibleState()) {
mPrimaryBouncerInteractor.showMessage(message, colorState);
}
}
@@ -1778,66 +1732,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
}
- /**
- * An opportunity for the AlternateBouncer to handle the touch instead of sending
- * the touch to NPVC child views.
- * @return true if the alternate bouncer should consime the touch and prevent it from
- * going to its child views
- */
- public boolean dispatchTouchEvent(MotionEvent event) {
- if (shouldInterceptTouchEvent(event)
- && !mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(event)) {
- onTouch(event);
- }
- return shouldInterceptTouchEvent(event);
- }
-
- /**
- * Whether the touch should be intercepted by the AlternateBouncer before going to the
- * notification shade's child views.
- */
- public boolean shouldInterceptTouchEvent(MotionEvent event) {
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- return false;
- }
- return mAlternateBouncerInteractor.isVisibleState();
- }
-
- /**
- * For any touches on the NPVC, show the primary bouncer if the alternate bouncer is currently
- * showing.
- */
- public boolean onTouch(MotionEvent event) {
- if (DeviceEntryUdfpsRefactor.isEnabled()) {
- return false;
- }
-
- boolean handleTouch = shouldInterceptTouchEvent(event);
- if (handleTouch) {
- final boolean actionDown = event.getActionMasked() == MotionEvent.ACTION_DOWN;
- final boolean actionDownThenUp = mAlternateBouncerInteractor.getReceivedDownTouch()
- && event.getActionMasked() == MotionEvent.ACTION_UP;
- final boolean udfpsOverlayWillForwardEventsOutsideNotificationShade =
- mKeyguardUpdateManager.isUdfpsEnrolled();
- final boolean actionOutsideShouldDismissAlternateBouncer =
- event.getActionMasked() == MotionEvent.ACTION_OUTSIDE
- && !udfpsOverlayWillForwardEventsOutsideNotificationShade;
- if (actionDown) {
- mAlternateBouncerInteractor.setReceivedDownTouch(true);
- } else if ((actionDownThenUp || actionOutsideShouldDismissAlternateBouncer)
- && mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()) {
- showPrimaryBouncer(true);
- }
- }
-
- // Forward NPVC touches to callbacks in case they want to respond to touches
- for (KeyguardViewManagerCallback callback: mCallbacks) {
- callback.onTouch(event);
- }
-
- return handleTouch;
- }
-
/** Update keyguard position based on a tapped X coordinate. */
public void updateKeyguardPosition(float x) {
mPrimaryBouncerInteractor.setKeyguardPosition(x);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
index 5f864e5dc53a..09e191dd1911 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -18,10 +18,12 @@ package com.android.systemui.statusbar.phone.dagger
import android.view.Display
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Default
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.core.CommandQueueInitializer
import com.android.systemui.statusbar.core.MultiDisplayStatusBarInitializerStore
+import com.android.systemui.statusbar.core.MultiDisplayStatusBarStarter
import com.android.systemui.statusbar.core.SingleDisplayStatusBarInitializerStore
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.core.StatusBarInitializer
@@ -29,7 +31,9 @@ import com.android.systemui.statusbar.core.StatusBarInitializerImpl
import com.android.systemui.statusbar.core.StatusBarInitializerStore
import com.android.systemui.statusbar.core.StatusBarOrchestrator
import com.android.systemui.statusbar.core.StatusBarSimpleFragment
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStoreImpl
import dagger.Binds
@@ -38,6 +42,7 @@ import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import kotlinx.coroutines.CoroutineScope
/** Similar in purpose to [StatusBarModule], but scoped only to phones */
@Module
@@ -58,24 +63,56 @@ interface StatusBarPhoneModule {
implFactory: StatusBarInitializerImpl.Factory
): StatusBarInitializer.Factory
- /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */
- @Binds
- @IntoMap
- @ClassKey(StatusBarInitializerImpl::class)
- fun bindStatusBarInitializer(@Default impl: StatusBarInitializerImpl): CoreStartable
-
@Binds fun statusBarInitializer(@Default impl: StatusBarInitializerImpl): StatusBarInitializer
companion object {
+ /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(StatusBarInitializer::class)
+ fun bindStatusBarInitializer(
+ @Default defaultInitializerLazy: Lazy<StatusBarInitializerImpl>
+ ): CoreStartable {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ // Will be started through MultiDisplayStatusBarStarter
+ CoreStartable.NOP
+ } else {
+ defaultInitializerLazy.get()
+ }
+ }
+
// Dagger doesn't support providing AssistedInject types, without a qualifier. Using the
// Default qualifier for this reason.
@Default
@Provides
@SysUISingleton
fun statusBarInitializerImpl(
- implFactory: StatusBarInitializerImpl.Factory
+ implFactory: StatusBarInitializerImpl.Factory,
+ statusBarWindowControllerStore: StatusBarWindowControllerStore,
): StatusBarInitializerImpl {
- return implFactory.create(displayId = Display.DEFAULT_DISPLAY)
+ return implFactory.create(statusBarWindowControllerStore.defaultDisplay)
+ }
+
+ @Provides
+ @SysUISingleton
+ @Default // Dagger does not support providing @AssistedInject types without a qualifier
+ fun orchestrator(
+ @Background backgroundApplicationScope: CoroutineScope,
+ statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+ statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+ initializerStore: StatusBarInitializerStore,
+ statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ statusBarOrchestratorFactory: StatusBarOrchestrator.Factory,
+ ): StatusBarOrchestrator {
+ return statusBarOrchestratorFactory.create(
+ Display.DEFAULT_DISPLAY,
+ backgroundApplicationScope,
+ statusBarWindowStateRepositoryStore.defaultDisplay,
+ statusBarModeRepositoryStore.defaultDisplay,
+ initializerStore.defaultDisplay,
+ statusBarWindowControllerStore.defaultDisplay,
+ )
}
@Provides
@@ -83,11 +120,29 @@ interface StatusBarPhoneModule {
@IntoMap
@ClassKey(StatusBarOrchestrator::class)
fun orchestratorCoreStartable(
- orchestratorLazy: Lazy<StatusBarOrchestrator>
+ @Default orchestratorLazy: Lazy<StatusBarOrchestrator>
): CoreStartable {
- return if (StatusBarSimpleFragment.isEnabled) {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ // Will be started through MultiDisplayStatusBarStarter
+ CoreStartable.NOP
+ } else if (StatusBarSimpleFragment.isEnabled) {
orchestratorLazy.get()
} else {
+ // Will be started through CentralSurfacesImpl
+ CoreStartable.NOP
+ }
+ }
+
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(MultiDisplayStatusBarStarter::class)
+ fun multiDisplayStarter(
+ multiDisplayStatusBarStarterLazy: Lazy<MultiDisplayStatusBarStarter>
+ ): CoreStartable {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ multiDisplayStatusBarStarterLazy.get()
+ } else {
CoreStartable.NOP
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index c258095510c6..d868519ee9e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -29,6 +29,7 @@ import android.telephony.SubscriptionManager;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.SparseArray;
+import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -55,7 +56,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.OperatorNameView;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips;
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.core.StatusBarSimpleFragment;
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
@@ -333,7 +334,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
- mDumpManager.registerDumpable(getClass().getSimpleName(), this);
+ mDumpManager.registerDumpable(getDumpableName(), this);
mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(
(PhoneStatusBarView) getView());
mStatusBarFragmentComponent.init();
@@ -374,6 +375,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mStatusBar, mCollapsedStatusBarViewModel, mStatusBarVisibilityChangeListener);
}
+ private String getDumpableName() {
+ if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) {
+ return getClass().getSimpleName();
+ } else {
+ return getClass().getSimpleName() + getContext().getDisplayId();
+ }
+ }
+
@Override
public void onCameraLaunchGestureDetected(int source) {
mWaitingForWindowStateChangeAfterCameraLaunch = true;
@@ -470,7 +479,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
startable.stop();
mStartableStates.put(startable, Startable.State.STOPPED);
}
- mDumpManager.unregisterDumpable(getClass().getSimpleName());
+ mDumpManager.unregisterDumpable(getDumpableName());
if (mNicBindingDisposable != null) {
mNicBindingDisposable.dispose();
mNicBindingDisposable = null;
@@ -486,7 +495,12 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
NotificationIconContainer notificationIcons =
notificationIconArea.requireViewById(R.id.notificationIcons);
mNotificationIconAreaInner = notificationIcons;
- mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
+ if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) {
+ //TODO(b/369337701): implement notification icons for all displays.
+ // Currently if we try to bind for all displays, there is a crash, because the same
+ // notification icon view can't have multiple parents.
+ mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
+ }
if (!StatusBarSimpleFragment.isEnabled()) {
updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false);
@@ -642,7 +656,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
boolean showSecondaryOngoingActivityChip =
Flags.statusBarScreenSharingChips()
- && StatusBarRonChips.isEnabled()
+ && StatusBarNotifChips.isEnabled()
&& mHasSecondaryOngoingActivity;
return new StatusBarVisibilityModel(
@@ -684,7 +698,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
boolean showSecondaryOngoingActivityChip =
// Secondary chips are only supported when RONs are enabled.
- StatusBarRonChips.isEnabled()
+ StatusBarNotifChips.isEnabled()
&& visibilityModel.getShowSecondaryOngoingActivityChip()
&& !disableNotifications;
if (showSecondaryOngoingActivityChip) {
@@ -811,7 +825,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
}
private void showSecondaryOngoingActivityChip(boolean animate) {
- StatusBarRonChips.assertInNewMode();
+ StatusBarNotifChips.assertInNewMode();
StatusBarSimpleFragment.assertInLegacyMode();
animateShow(mSecondaryOngoingActivityChip, animate);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index bad6f80c3735..694a5e529ec4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
import androidx.annotation.VisibleForTesting
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlagsClassic
@@ -29,8 +29,8 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
@@ -116,7 +116,7 @@ constructor(
private fun createViewModel(subId: Int): Pair<MobileIconViewModel, CoroutineScope> {
// Create a child scope so we can cancel it
- val vmScope = scope.createChildScope(createCoroutineTracingContext("MobileIconViewModel"))
+ val vmScope = scope.createChildScope(newTracingContext("MobileIconViewModel"))
val vm =
MobileIconViewModel(
subId,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index 9c168be0693f..3a07d9b6beaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -27,6 +27,7 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.core.StatusBarSimpleFragment
@@ -83,7 +84,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa
}
}
- if (Flags.statusBarScreenSharingChips() && !Flags.statusBarRonChips()) {
+ if (Flags.statusBarScreenSharingChips() && !StatusBarNotifChips.isEnabled) {
val primaryChipView: View =
view.requireViewById(R.id.ongoing_activity_chip_primary)
launch {
@@ -119,7 +120,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa
}
}
- if (Flags.statusBarScreenSharingChips() && Flags.statusBarRonChips()) {
+ if (Flags.statusBarScreenSharingChips() && StatusBarNotifChips.isEnabled) {
val primaryChipView: View =
view.requireViewById(R.id.ongoing_activity_chip_primary)
val secondaryChipView: View =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
index 7d0dadcf8c6e..7a88dcd92b88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
@@ -17,78 +17,40 @@
package com.android.systemui.statusbar.window
import android.content.Context
-import android.view.Display
import android.view.WindowManager
import com.android.app.viewcapture.ViewCaptureAwareWindowManager
-import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
+import com.android.systemui.display.data.repository.PerDisplayStore
+import com.android.systemui.display.data.repository.PerDisplayStoreImpl
+import com.android.systemui.display.data.repository.SingleDisplayStore
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
-import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
/** Store that allows to retrieve per display instances of [StatusBarWindowController]. */
-interface StatusBarWindowControllerStore {
- /**
- * The instance for the default/main display of the device. For example, on a phone or a tablet,
- * the default display is the internal/built-in display of the device.
- *
- * Note that the id of the default display is [Display.DEFAULT_DISPLAY].
- */
- val defaultDisplay: StatusBarWindowController
-
- /**
- * Returns an instance for a specific display id.
- *
- * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing
- * displays.
- */
- fun forDisplay(displayId: Int): StatusBarWindowController
-}
+interface StatusBarWindowControllerStore : PerDisplayStore<StatusBarWindowController>
@SysUISingleton
class MultiDisplayStatusBarWindowControllerStore
@Inject
constructor(
- @Background private val backgroundApplicationScope: CoroutineScope,
+ @Background backgroundApplicationScope: CoroutineScope,
private val controllerFactory: StatusBarWindowController.Factory,
private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
private val viewCaptureAwareWindowManagerFactory: ViewCaptureAwareWindowManager.Factory,
- private val displayRepository: DisplayRepository,
-) : StatusBarWindowControllerStore, CoreStartable {
+ displayRepository: DisplayRepository,
+) :
+ StatusBarWindowControllerStore,
+ PerDisplayStoreImpl<StatusBarWindowController>(backgroundApplicationScope, displayRepository) {
init {
StatusBarConnectedDisplays.assertInNewMode()
}
- private val perDisplayControllers = ConcurrentHashMap<Int, StatusBarWindowController>()
-
- override fun start() {
- backgroundApplicationScope.launch(CoroutineName("StatusBarWindowController#start")) {
- displayRepository.displayRemovalEvent.collect { displayId ->
- perDisplayControllers.remove(displayId)
- }
- }
- }
-
- override val defaultDisplay: StatusBarWindowController
- get() = forDisplay(Display.DEFAULT_DISPLAY)
-
- override fun forDisplay(displayId: Int): StatusBarWindowController {
- if (displayRepository.getDisplay(displayId) == null) {
- throw IllegalArgumentException("Display with id $displayId doesn't exist.")
- }
- return perDisplayControllers.computeIfAbsent(displayId) {
- createControllerForDisplay(displayId)
- }
- }
-
- private fun createControllerForDisplay(displayId: Int): StatusBarWindowController {
+ override fun createInstanceForDisplay(displayId: Int): StatusBarWindowController {
val statusBarDisplayContext =
displayWindowPropertiesRepository.get(
displayId = displayId,
@@ -101,6 +63,8 @@ constructor(
viewCaptureAwareWindowManager,
)
}
+
+ override val instanceClass = StatusBarWindowController::class.java
}
@SysUISingleton
@@ -110,16 +74,13 @@ constructor(
context: Context,
viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
factory: StatusBarWindowControllerImpl.Factory,
-) : StatusBarWindowControllerStore {
+) :
+ StatusBarWindowControllerStore,
+ PerDisplayStore<StatusBarWindowController> by SingleDisplayStore(
+ factory.create(context, viewCaptureAwareWindowManager)
+ ) {
init {
StatusBarConnectedDisplays.assertInLegacyMode()
}
-
- private val controller: StatusBarWindowController =
- factory.create(context, viewCaptureAwareWindowManager)
-
- override val defaultDisplay = controller
-
- override fun forDisplay(displayId: Int) = controller
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
index 75c66f234bdc..90c005139c56 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt
@@ -67,7 +67,7 @@ class DistanceBasedGestureRecognizerProvider(
val distanceThresholdPx =
resources.getDimensionPixelSize(
com.android.internal.R.dimen.system_gestures_distance_threshold
- )
+ ) * 5
return remember(distanceThresholdPx) {
recognizerFactory(distanceThresholdPx, gestureStateChangedCallback)
}
@@ -77,7 +77,8 @@ class DistanceBasedGestureRecognizerProvider(
fun GestureState.toTutorialActionState(): TutorialActionState {
return when (this) {
NotStarted -> TutorialActionState.NotStarted
- is InProgress -> TutorialActionState.InProgress(progress)
+ // progress is disabled for now as views are not ready to handle varying progress
+ is InProgress -> TutorialActionState.InProgress(0f)
Finished -> TutorialActionState.Finished
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt
index 56e97a357d67..80f800390852 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizer.kt
@@ -16,10 +16,14 @@
package com.android.systemui.touchpad.tutorial.ui.gesture
+import android.util.MathUtils
import android.view.MotionEvent
import kotlin.math.abs
-/** Recognizes touchpad back gesture, that is three fingers swiping left or right */
+/**
+ * Recognizes touchpad back gesture, that is - using three fingers on touchpad - swiping left or
+ * right.
+ */
class BackGestureRecognizer(private val gestureDistanceThresholdPx: Int) : GestureRecognizer {
private val distanceTracker = DistanceTracker()
@@ -36,7 +40,7 @@ class BackGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu
gestureStateChangedCallback,
gestureState,
isFinished = { abs(it.deltaX) >= gestureDistanceThresholdPx },
- progress = { 0f },
+ progress = { MathUtils.saturate(abs(it.deltaX / gestureDistanceThresholdPx)) },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
index 3db9d7ccc8f7..2b84a4c50613 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt
@@ -16,9 +16,10 @@
package com.android.systemui.touchpad.tutorial.ui.gesture
+import android.util.MathUtils
import android.view.MotionEvent
-/** Recognizes touchpad home gesture, that is three fingers swiping up */
+/** Recognizes touchpad home gesture, that is - using three fingers on touchpad - swiping up. */
class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : GestureRecognizer {
private val distanceTracker = DistanceTracker()
@@ -35,7 +36,7 @@ class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu
gestureStateChangedCallback,
gestureState,
isFinished = { -it.deltaY >= gestureDistanceThresholdPx },
- progress = { 0f },
+ progress = { MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx) },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
index a194ad6a8016..69b7c5edd750 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizer.kt
@@ -16,13 +16,14 @@
package com.android.systemui.touchpad.tutorial.ui.gesture
+import android.util.MathUtils
import android.view.MotionEvent
import kotlin.math.abs
/**
- * Recognizes apps gesture completion. That is - using three fingers on touchpad - swipe up over
- * some distance threshold and then slow down gesture before fingers are lifted. Implementation is
- * based on [com.android.quickstep.util.TriggerSwipeUpTouchTracker]
+ * Recognizes recent apps gesture, that is - using three fingers on touchpad - swipe up over some
+ * distance threshold and then slow down gesture before fingers are lifted. Implementation is based
+ * on [com.android.quickstep.util.TriggerSwipeUpTouchTracker]
*/
class RecentAppsGestureRecognizer(
private val gestureDistanceThresholdPx: Int,
@@ -49,7 +50,7 @@ class RecentAppsGestureRecognizer(
-state.deltaY >= gestureDistanceThresholdPx &&
abs(velocityTracker.calculateVelocity().value) <= velocityThresholdPxPerMs
},
- progress = { 0f },
+ progress = { MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx) },
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 213581787f73..d0817d736f07 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -61,7 +61,6 @@ public class TunerActivity extends Activity implements
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setTheme(androidx.appcompat.R.style.Theme_AppCompat_DayNight);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
requestWindowFeature(Window.FEATURE_NO_TITLE);
diff --git a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
index de9231856e2c..d87607dec8a1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
@@ -11,6 +11,7 @@ import android.graphics.drawable.AnimatedStateListDrawable
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
import android.util.Log
import androidx.annotation.Px
import com.android.app.tracing.traceSection
@@ -22,36 +23,35 @@ class DrawableSize {
const val TAG = "SysUiDrawableSize"
/**
- * Downscales passed Drawable to set maximum width and height. This will only
- * be done for Drawables that can be downscaled non-destructively - e.g. animated
- * and stateful drawables will no be downscaled.
+ * Downscales passed Drawable to set maximum width and height. This will only be done for
+ * Drawables that can be downscaled non-destructively - e.g. animated drawables, stateful
+ * drawables, and drawables with mixed-type layers will not be downscaled.
*
- * Downscaling will keep the aspect ratio.
- * This method will not touch drawables that already fit into size specification.
+ * Downscaling will keep the aspect ratio. This method will not touch drawables that already
+ * fit into size specification.
*
* @param resources Resources on which to base the density of resized drawable.
* @param drawable Drawable to downscale.
* @param maxWidth Maximum width of the downscaled drawable.
* @param maxHeight Maximum height of the downscaled drawable.
- *
* @return returns downscaled drawable if it's possible to downscale it or original if it's
- * not.
+ * not.
*/
@JvmStatic
fun downscaleToSize(
res: Resources,
drawable: Drawable,
@Px maxWidth: Int,
- @Px maxHeight: Int
+ @Px maxHeight: Int,
): Drawable {
traceSection("DrawableSize#downscaleToSize") {
// Bitmap drawables can contain big bitmaps as their content while sneaking it past
// us using density scaling. Inspect inside the Bitmap drawables for actual bitmap
// size for those.
- val originalWidth = (drawable as? BitmapDrawable)?.bitmap?.width
- ?: drawable.intrinsicWidth
- val originalHeight = (drawable as? BitmapDrawable)?.bitmap?.height
- ?: drawable.intrinsicHeight
+ val originalWidth =
+ (drawable as? BitmapDrawable)?.bitmap?.width ?: drawable.intrinsicWidth
+ val originalHeight =
+ (drawable as? BitmapDrawable)?.bitmap?.height ?: drawable.intrinsicHeight
// Don't touch drawable if we can't resolve sizes for whatever reason.
if (originalWidth <= 0 || originalHeight <= 0) {
@@ -61,14 +61,18 @@ class DrawableSize {
// Do not touch drawables that are already within bounds.
if (originalWidth < maxWidth && originalHeight < maxHeight) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Not resizing $originalWidth x $originalHeight" + " " +
- "to $maxWidth x $maxHeight")
+ Log.d(
+ TAG,
+ "Not resizing $originalWidth x $originalHeight" +
+ " " +
+ "to $maxWidth x $maxHeight",
+ )
}
return drawable
}
- if (!isSimpleBitmap(drawable)) {
+ if (isComplicatedBitmap(drawable)) {
return drawable
}
@@ -80,19 +84,25 @@ class DrawableSize {
val height = (originalHeight * scale).toInt()
if (width <= 0 || height <= 0) {
- Log.w(TAG, "Attempted to resize ${drawable.javaClass.simpleName} " +
- "from $originalWidth x $originalHeight to invalid $width x $height.")
+ Log.w(
+ TAG,
+ "Attempted to resize ${drawable.javaClass.simpleName} " +
+ "from $originalWidth x $originalHeight to invalid $width x $height.",
+ )
return drawable
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Resizing large drawable (${drawable.javaClass.simpleName}) " +
- "from $originalWidth x $originalHeight to $width x $height")
+ Log.d(
+ TAG,
+ "Resizing large drawable (${drawable.javaClass.simpleName}) " +
+ "from $originalWidth x $originalHeight to $width x $height",
+ )
}
// We want to keep existing config if it's more efficient than 32-bit RGB.
- val config = (drawable as? BitmapDrawable)?.bitmap?.config
- ?: Bitmap.Config.ARGB_8888
+ val config =
+ (drawable as? BitmapDrawable)?.bitmap?.config ?: Bitmap.Config.ARGB_8888
val scaledDrawableBitmap = Bitmap.createBitmap(width, height, config)
val canvas = Canvas(scaledDrawableBitmap)
@@ -105,8 +115,8 @@ class DrawableSize {
}
}
- private fun isSimpleBitmap(drawable: Drawable): Boolean {
- return !(drawable.isStateful || isAnimated(drawable))
+ private fun isComplicatedBitmap(drawable: Drawable): Boolean {
+ return drawable.isStateful || isAnimated(drawable) || hasComplicatedLayers(drawable)
}
private fun isAnimated(drawable: Drawable): Boolean {
@@ -119,5 +129,30 @@ class DrawableSize {
drawable is AnimatedStateListDrawable ||
drawable is AnimatedVectorDrawable
}
+
+ private fun hasComplicatedLayers(drawable: Drawable): Boolean {
+ if (drawable !is LayerDrawable) {
+ return false
+ }
+ if (drawable.numberOfLayers == 1) {
+ return false
+ }
+
+ val firstLayerType = drawable.getDrawable(0).javaClass
+ for (i in 1..<drawable.numberOfLayers) {
+ val layer = drawable.getDrawable(i)
+ if (layer.javaClass != firstLayerType) {
+ // If different layers have different drawable types, we shouldn't scale it down
+ // because we may lose the level information if one of the layers is a bitmap
+ // and another layer is a level-list. See b/244282477.
+ return true
+ }
+ if (isComplicatedBitmap(layer)) {
+ return true
+ }
+ }
+
+ return false
+ }
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt
index 2af84c7e46f0..579af73236f3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt
@@ -16,10 +16,9 @@
package com.android.systemui.util.kotlin
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dagger.qualifiers.Tracing
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
@@ -27,7 +26,6 @@ import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
/** Providers for various application-wide coroutines-related constructs. */
@Module
@@ -35,16 +33,15 @@ class GlobalCoroutinesModule {
@Provides
@Singleton
@Application
- fun applicationScope(
- @Main dispatcherContext: CoroutineContext,
- ): CoroutineScope = CoroutineScope(dispatcherContext + createCoroutineTracingContext("ApplicationScope"))
+ fun applicationScope(@Main dispatcherContext: CoroutineContext): CoroutineScope =
+ CoroutineScope(dispatcherContext + newTracingContext("ApplicationScope"))
@Provides
@Singleton
@Main
@Deprecated(
"Use @Main CoroutineContext instead",
- ReplaceWith("mainCoroutineContext()", "kotlin.coroutines.CoroutineContext")
+ ReplaceWith("mainCoroutineContext()", "kotlin.coroutines.CoroutineContext"),
)
fun mainDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
index 4d9aaa6dc6b0..ea8709f7d65c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
@@ -15,7 +15,6 @@
*/
package com.android.systemui.util.settings
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
import android.annotation.UserIdInt
import android.content.ContentResolver
import android.database.ContentObserver
@@ -24,6 +23,7 @@ import android.provider.Settings.SettingNotFoundException
import androidx.annotation.AnyThread
import androidx.annotation.WorkerThread
import com.android.app.tracing.TraceUtils.trace
+import com.android.systemui.coroutines.newTracingContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -94,7 +94,7 @@ interface SettingsProxy {
*/
@AnyThread
fun registerContentObserverAsync(name: String, settingsObserver: ContentObserver) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-A")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-A")).launch {
registerContentObserverSync(getUriFor(name), settingsObserver)
}
@@ -109,9 +109,9 @@ interface SettingsProxy {
fun registerContentObserverAsync(
name: String,
settingsObserver: ContentObserver,
- @WorkerThread registered: Runnable
+ @WorkerThread registered: Runnable,
) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-B")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-B")).launch {
registerContentObserverSync(getUriFor(name), settingsObserver)
registered.run()
}
@@ -144,7 +144,7 @@ interface SettingsProxy {
*/
@AnyThread
fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-C")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-C")).launch {
registerContentObserverSync(uri, settingsObserver)
}
@@ -159,9 +159,9 @@ interface SettingsProxy {
fun registerContentObserverAsync(
uri: Uri,
settingsObserver: ContentObserver,
- @WorkerThread registered: Runnable
+ @WorkerThread registered: Runnable,
) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-D")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-D")).launch {
registerContentObserverSync(uri, settingsObserver)
registered.run()
}
@@ -175,7 +175,7 @@ interface SettingsProxy {
fun registerContentObserverSync(
name: String,
notifyForDescendants: Boolean,
- settingsObserver: ContentObserver
+ settingsObserver: ContentObserver,
) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
/**
@@ -204,9 +204,9 @@ interface SettingsProxy {
fun registerContentObserverAsync(
name: String,
notifyForDescendants: Boolean,
- settingsObserver: ContentObserver
+ settingsObserver: ContentObserver,
) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-E")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-E")).launch {
registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
}
@@ -222,9 +222,9 @@ interface SettingsProxy {
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- @WorkerThread registered: Runnable
+ @WorkerThread registered: Runnable,
) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-F")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-F")).launch {
registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
registered.run()
}
@@ -273,9 +273,9 @@ interface SettingsProxy {
fun registerContentObserverAsync(
uri: Uri,
notifyForDescendants: Boolean,
- settingsObserver: ContentObserver
+ settingsObserver: ContentObserver,
) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-G")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-G")).launch {
registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
}
@@ -291,9 +291,9 @@ interface SettingsProxy {
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- @WorkerThread registered: Runnable
+ @WorkerThread registered: Runnable,
) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-H")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-H")).launch {
registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
registered.run()
}
@@ -330,7 +330,9 @@ interface SettingsProxy {
*/
@AnyThread
fun unregisterContentObserverAsync(settingsObserver: ContentObserver) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-I")).launch { unregisterContentObserver(settingsObserver) }
+ CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-I")).launch {
+ unregisterContentObserver(settingsObserver)
+ }
/**
* Look up a name in the database.
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
index c820c07b61b1..c5deca214e28 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
@@ -15,7 +15,6 @@
*/
package com.android.systemui.util.settings
-import com.android.app.tracing.coroutines.createCoroutineTracingContext
import android.annotation.UserIdInt
import android.annotation.WorkerThread
import android.content.ContentResolver
@@ -24,6 +23,7 @@ import android.net.Uri
import android.os.UserHandle
import android.provider.Settings.SettingNotFoundException
import com.android.app.tracing.TraceUtils.trace
+import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloat
import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrThrow
import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow
@@ -79,7 +79,7 @@ interface UserSettingsProxy : SettingsProxy {
}
override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-A")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-A")).launch {
registerContentObserverForUserSync(uri, settingsObserver, userId)
}
@@ -88,7 +88,7 @@ interface UserSettingsProxy : SettingsProxy {
override fun registerContentObserverSync(
uri: Uri,
notifyForDescendants: Boolean,
- settingsObserver: ContentObserver
+ settingsObserver: ContentObserver,
) {
registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
}
@@ -111,9 +111,9 @@ interface UserSettingsProxy : SettingsProxy {
override fun registerContentObserverAsync(
uri: Uri,
notifyForDescendants: Boolean,
- settingsObserver: ContentObserver
+ settingsObserver: ContentObserver,
) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-B")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-B")).launch {
registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
}
@@ -156,9 +156,9 @@ interface UserSettingsProxy : SettingsProxy {
fun registerContentObserverForUserAsync(
name: String,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-C")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-C")).launch {
registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
}
@@ -197,9 +197,9 @@ interface UserSettingsProxy : SettingsProxy {
fun registerContentObserverForUserAsync(
uri: Uri,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-D")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-D")).launch {
registerContentObserverForUserSync(uri, settingsObserver, userHandle)
}
@@ -214,9 +214,9 @@ interface UserSettingsProxy : SettingsProxy {
uri: Uri,
settingsObserver: ContentObserver,
userHandle: Int,
- @WorkerThread registered: Runnable
+ @WorkerThread registered: Runnable,
) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-E")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-E")).launch {
registerContentObserverForUserSync(uri, settingsObserver, userHandle)
registered.run()
}
@@ -273,14 +273,14 @@ interface UserSettingsProxy : SettingsProxy {
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) {
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-F")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-F")).launch {
registerContentObserverForUserSync(
getUriFor(name),
notifyForDescendants,
settingsObserver,
- userHandle
+ userHandle,
)
}
}
@@ -337,14 +337,14 @@ interface UserSettingsProxy : SettingsProxy {
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
- userHandle: Int
+ userHandle: Int,
) =
- CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-G")).launch {
+ CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-G")).launch {
registerContentObserverForUserSync(
uri,
notifyForDescendants,
settingsObserver,
- userHandle
+ userHandle,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt
index 2dd0bdab93d1..5e0af634c786 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt
@@ -17,7 +17,7 @@
package com.android.systemui.volume.dialog.settings.domain
import android.app.ActivityManager
-import com.android.app.tracing.coroutines.flow.map
+import com.android.app.tracing.coroutines.flow.flowName
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
@@ -30,6 +30,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@VolumeDialogScope
@@ -49,6 +50,7 @@ constructor(
deviceProvisionedController.isCurrentUserSetup() &&
model.lockTaskModeState == ActivityManager.LOCK_TASK_MODE_NONE
}
+ .flowName("VDSBI#isVisible")
.stateIn(coroutineScope, SharingStarted.Eagerly, false)
fun onButtonClicked() {
diff --git a/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml b/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml
new file mode 100644
index 000000000000..d9ea0b91abdf
--- /dev/null
+++ b/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:drawable="@drawable/ic_brightness"/>
+ <item android:drawable="@drawable/ic_brightness"/>
+</layer-list>
diff --git a/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml b/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml
new file mode 100644
index 000000000000..796de8f15f47
--- /dev/null
+++ b/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+ <!-- dessert_flan is a PNG while ic_brightness is a level-list. -->
+ <item android:drawable="@drawable/dessert_flan"/>
+ <item android:drawable="@drawable/ic_brightness"/>
+</layer-list>
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java
deleted file mode 100644
index c51aa04fc7b6..000000000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2022 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.keyguard;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.AnimatedStateListDrawable;
-import android.util.Pair;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.ImageView;
-
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.biometrics.AuthRippleController;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
-import com.android.systemui.doze.util.BurnInHelperKt;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.After;
-import org.junit.Before;
-import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoSession;
-import org.mockito.quality.Strictness;
-
-public class LegacyLockIconViewControllerBaseTest extends SysuiTestCase {
- protected static final String UNLOCKED_LABEL = "unlocked";
- protected static final String LOCKED_LABEL = "locked";
- protected static final int PADDING = 10;
-
- protected MockitoSession mStaticMockSession;
-
- protected final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
- protected @Mock DeviceEntryInteractor mDeviceEntryInteractor;
- protected @Mock LockIconView mLockIconView;
- protected @Mock ImageView mLockIcon;
- protected @Mock AnimatedStateListDrawable mIconDrawable;
- protected @Mock Context mContext;
- protected @Mock Resources mResources;
- protected @Mock(answer = Answers.RETURNS_DEEP_STUBS) WindowManager mWindowManager;
- protected @Mock StatusBarStateController mStatusBarStateController;
- protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- protected @Mock KeyguardViewController mKeyguardViewController;
- protected @Mock KeyguardStateController mKeyguardStateController;
- protected @Mock FalsingManager mFalsingManager;
- protected @Mock AuthController mAuthController;
- protected @Mock DumpManager mDumpManager;
- protected @Mock AccessibilityManager mAccessibilityManager;
- protected @Mock ConfigurationController mConfigurationController;
- protected @Mock VibratorHelper mVibrator;
- protected @Mock AuthRippleController mAuthRippleController;
- protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
- protected FakeFeatureFlags mFeatureFlags;
-
- protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
-
- protected LegacyLockIconViewController mUnderTest;
-
- // Capture listeners so that they can be used to send events
- @Captor protected ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor =
- ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
-
- @Captor protected ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateCaptor =
- ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
- protected KeyguardStateController.Callback mKeyguardStateCallback;
-
- @Captor protected ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateCaptor =
- ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
- protected StatusBarStateController.StateListener mStatusBarStateListener;
-
- @Captor protected ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor;
- protected AuthController.Callback mAuthControllerCallback;
-
- @Captor protected ArgumentCaptor<KeyguardUpdateMonitorCallback>
- mKeyguardUpdateMonitorCallbackCaptor =
- ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
- protected KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback;
-
- @Captor protected ArgumentCaptor<Point> mPointCaptor;
-
- @Before
- public void setUp() throws Exception {
- mStaticMockSession = mockitoSession()
- .mockStatic(BurnInHelperKt.class)
- .strictness(Strictness.LENIENT)
- .startMocking();
- MockitoAnnotations.initMocks(this);
-
- setupLockIconViewMocks();
- when(mContext.getResources()).thenReturn(mResources);
- when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager);
- Rect windowBounds = new Rect(0, 0, 800, 1200);
- when(mWindowManager.getCurrentWindowMetrics().getBounds()).thenReturn(windowBounds);
- when(mResources.getString(R.string.accessibility_unlock_button)).thenReturn(UNLOCKED_LABEL);
- when(mResources.getString(R.string.accessibility_lock_icon)).thenReturn(LOCKED_LABEL);
- when(mResources.getDrawable(anyInt(), any())).thenReturn(mIconDrawable);
- when(mResources.getDimensionPixelSize(R.dimen.lock_icon_padding)).thenReturn(PADDING);
- when(mAuthController.getScaleFactor()).thenReturn(1f);
-
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
- when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
-
- if (!SceneContainerFlag.isEnabled()) {
- mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
- //TODO move this to use @DisableFlags annotation if needed
- mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
- }
-
- mFeatureFlags = new FakeFeatureFlags();
- mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
- mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false);
-
- mUnderTest = new LegacyLockIconViewController(
- mStatusBarStateController,
- mKeyguardUpdateMonitor,
- mKeyguardViewController,
- mKeyguardStateController,
- mFalsingManager,
- mAuthController,
- mDumpManager,
- mAccessibilityManager,
- mConfigurationController,
- mDelayableExecutor,
- mVibrator,
- mAuthRippleController,
- mResources,
- mKosmos.getKeyguardTransitionInteractor(),
- KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
- mFeatureFlags,
- mPrimaryBouncerInteractor,
- mContext,
- () -> mDeviceEntryInteractor
- );
- }
-
- @After
- public void tearDown() {
- mStaticMockSession.finishMocking();
- }
-
- protected Pair<Float, Point> setupUdfps() {
- when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
- final Point udfpsLocation = new Point(50, 75);
- final float radius = 33f;
- when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation);
- when(mAuthController.getUdfpsRadius()).thenReturn(radius);
-
- return new Pair(radius, udfpsLocation);
- }
-
- protected void setupShowLockIcon() {
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
- when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mStatusBarStateController.getDozeAmount()).thenReturn(0f);
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
- when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
- }
-
- protected void captureAuthControllerCallback() {
- verify(mAuthController).addCallback(mAuthControllerCallbackCaptor.capture());
- mAuthControllerCallback = mAuthControllerCallbackCaptor.getValue();
- }
-
- protected void captureKeyguardStateCallback() {
- verify(mKeyguardStateController).addCallback(mKeyguardStateCaptor.capture());
- mKeyguardStateCallback = mKeyguardStateCaptor.getValue();
- }
-
- protected void captureStatusBarStateListener() {
- verify(mStatusBarStateController).addCallback(mStatusBarStateCaptor.capture());
- mStatusBarStateListener = mStatusBarStateCaptor.getValue();
- }
-
- protected void captureKeyguardUpdateMonitorCallback() {
- verify(mKeyguardUpdateMonitor).registerCallback(
- mKeyguardUpdateMonitorCallbackCaptor.capture());
- mKeyguardUpdateMonitorCallback = mKeyguardUpdateMonitorCallbackCaptor.getValue();
- }
-
- protected void setupLockIconViewMocks() {
- when(mLockIconView.getResources()).thenReturn(mResources);
- when(mLockIconView.getContext()).thenReturn(mContext);
- when(mLockIconView.getLockIcon()).thenReturn(mLockIcon);
- }
-
- protected void resetLockIconView() {
- reset(mLockIconView);
- setupLockIconViewMocks();
- }
-
- protected void init(boolean useDozeMigrationFlag) {
- mFeatureFlags.set(DOZING_MIGRATION_1, useDozeMigrationFlag);
- mUnderTest.setLockIconView(mLockIconView);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
deleted file mode 100644
index c1ba39e89cf9..000000000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-
-import static com.android.keyguard.LockIconView.ICON_LOCK;
-import static com.android.keyguard.LockIconView.ICON_UNLOCK;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.graphics.Point;
-import android.hardware.biometrics.BiometricSourceType;
-import android.testing.TestableLooper;
-import android.util.Pair;
-import android.view.HapticFeedbackConstants;
-import android.view.View;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.biometrics.UdfpsController;
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
-import com.android.systemui.doze.util.BurnInHelperKt;
-import com.android.systemui.flags.EnableSceneContainer;
-import com.android.systemui.statusbar.StatusBarState;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@TestableLooper.RunWithLooper
-public class LegacyLockIconViewControllerTest extends LegacyLockIconViewControllerBaseTest {
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- when(mLockIconView.isAttachedToWindow()).thenReturn(true);
- }
-
- @Test
- public void testUpdateFingerprintLocationOnInit() {
- // GIVEN fp sensor location is available pre-attached
- Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location
-
- // WHEN lock icon view controller is initialized and attached
- init(/* useMigrationFlag= */false);
-
- // THEN lock icon view location is updated to the udfps location with UDFPS radius
- verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
- eq(PADDING));
- }
-
- @Test
- public void testUpdatePaddingBasedOnResolutionScale() {
- // GIVEN fp sensor location is available pre-attached & scaled resolution factor is 5
- Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location
- when(mAuthController.getScaleFactor()).thenReturn(5f);
-
- // WHEN lock icon view controller is initialized and attached
- init(/* useMigrationFlag= */false);
-
- // THEN lock icon view location is updated with the scaled radius
- verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
- eq(PADDING * 5));
- }
-
- @Test
- public void testUpdateLockIconLocationOnAuthenticatorsRegistered() {
- // GIVEN fp sensor location is not available pre-init
- when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
- when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
- init(/* useMigrationFlag= */false);
- resetLockIconView(); // reset any method call counts for when we verify method calls later
-
- // GIVEN fp sensor location is available post-attached
- captureAuthControllerCallback();
- Pair<Float, Point> udfps = setupUdfps();
-
- // WHEN all authenticators are registered
- mAuthControllerCallback.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT);
- mDelayableExecutor.runAllReady();
-
- // THEN lock icon view location is updated with the same coordinates as auth controller vals
- verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
- eq(PADDING));
- }
-
- @Test
- public void testUpdateLockIconLocationOnUdfpsLocationChanged() {
- // GIVEN fp sensor location is not available pre-init
- when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
- when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
- init(/* useMigrationFlag= */false);
- resetLockIconView(); // reset any method call counts for when we verify method calls later
-
- // GIVEN fp sensor location is available post-attached
- captureAuthControllerCallback();
- Pair<Float, Point> udfps = setupUdfps();
-
- // WHEN udfps location changes
- mAuthControllerCallback.onUdfpsLocationChanged(new UdfpsOverlayParams());
- mDelayableExecutor.runAllReady();
-
- // THEN lock icon view location is updated with the same coordinates as auth controller vals
- verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
- eq(PADDING));
- }
-
- @Test
- public void testLockIconViewBackgroundEnabledWhenUdfpsIsSupported() {
- // GIVEN Udpfs sensor location is available
- setupUdfps();
-
- // WHEN the view is attached
- init(/* useMigrationFlag= */false);
-
- // THEN the lock icon view background should be enabled
- verify(mLockIconView).setUseBackground(true);
- }
-
- @Test
- public void testLockIconViewBackgroundDisabledWhenUdfpsIsNotSupported() {
- // GIVEN Udfps sensor location is not supported
- when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
-
- // WHEN the view is attached
- init(/* useMigrationFlag= */false);
-
- // THEN the lock icon view background should be disabled
- verify(mLockIconView).setUseBackground(false);
- }
-
- @Test
- public void testLockIconStartState() {
- // GIVEN lock icon state
- setupShowLockIcon();
-
- // WHEN lock icon controller is initialized
- init(/* useMigrationFlag= */false);
-
- // THEN the lock icon should show
- verify(mLockIconView).updateIcon(ICON_LOCK, false);
- }
-
- @Test
- public void testLockIcon_updateToUnlock() {
- // GIVEN starting state for the lock icon
- setupShowLockIcon();
-
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureKeyguardStateCallback();
- reset(mLockIconView);
-
- // WHEN the unlocked state changes to canDismissLockScreen=true
- when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
- mKeyguardStateCallback.onUnlockedChanged();
-
- // THEN the unlock should show
- verify(mLockIconView).updateIcon(ICON_UNLOCK, false);
- }
-
- @Test
- public void testLockIcon_clearsIconWhenUnlocked() {
- // GIVEN udfps not enrolled
- setupUdfps();
- when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false);
-
- // GIVEN starting state for the lock icon
- setupShowLockIcon();
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
-
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureStatusBarStateListener();
- reset(mLockIconView);
-
- // WHEN the dozing state changes
- mStatusBarStateListener.onDozingChanged(false /* isDozing */);
-
- // THEN the icon is cleared
- verify(mLockIconView).clearIcon();
- }
-
- @Test
- public void testLockIcon_updateToAodLock_whenUdfpsEnrolled() {
- // GIVEN udfps enrolled
- setupUdfps();
- when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
-
- // GIVEN starting state for the lock icon
- setupShowLockIcon();
-
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureStatusBarStateListener();
- reset(mLockIconView);
-
- // WHEN the dozing state changes
- mStatusBarStateListener.onDozingChanged(true /* isDozing */);
-
- // THEN the AOD lock icon should show
- verify(mLockIconView).updateIcon(ICON_LOCK, true);
- }
-
- @Test
- public void testBurnInOffsetsUpdated_onDozeAmountChanged() {
- // GIVEN udfps enrolled
- setupUdfps();
- when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
-
- // GIVEN burn-in offset = 5
- int burnInOffset = 5;
- when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset);
-
- // GIVEN starting state for the lock icon (keyguard)
- setupShowLockIcon();
- init(/* useMigrationFlag= */false);
- captureStatusBarStateListener();
- reset(mLockIconView);
-
- // WHEN dozing updates
- mStatusBarStateListener.onDozingChanged(true /* isDozing */);
- mStatusBarStateListener.onDozeAmountChanged(1f, 1f);
-
- // THEN the view's translation is updated to use the AoD burn-in offsets
- verify(mLockIconView).setTranslationY(burnInOffset);
- verify(mLockIconView).setTranslationX(burnInOffset);
- reset(mLockIconView);
-
- // WHEN the device is no longer dozing
- mStatusBarStateListener.onDozingChanged(false /* isDozing */);
- mStatusBarStateListener.onDozeAmountChanged(0f, 0f);
-
- // THEN the view is updated to NO translation (no burn-in offsets anymore)
- verify(mLockIconView).setTranslationY(0);
- verify(mLockIconView).setTranslationX(0);
- }
-
- @Test
- public void lockIconShows_afterUnlockStateChanges() {
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureKeyguardStateCallback();
- captureKeyguardUpdateMonitorCallback();
-
- // GIVEN user has unlocked with a biometric auth (ie: face auth)
- // and biometric running state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
- mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
- BiometricSourceType.FACE);
- reset(mLockIconView);
-
- // WHEN the unlocked state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
- mKeyguardStateCallback.onUnlockedChanged();
-
- // THEN the lock icon is shown
- verify(mLockIconView).setContentDescription(LOCKED_LABEL);
- }
-
- @Test
- public void lockIconAccessibility_notVisibleToUser() {
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureKeyguardStateCallback();
- captureKeyguardUpdateMonitorCallback();
-
- // GIVEN user has unlocked with a biometric auth (ie: face auth)
- // and biometric running state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
- mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
- BiometricSourceType.FACE);
- reset(mLockIconView);
- when(mLockIconView.isVisibleToUser()).thenReturn(false);
-
- // WHEN the unlocked state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
- mKeyguardStateCallback.onUnlockedChanged();
-
- // THEN the lock icon is shown
- verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- }
-
- @Test
- public void lockIconAccessibility_bouncerAnimatingAway() {
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureKeyguardStateCallback();
- captureKeyguardUpdateMonitorCallback();
-
- // GIVEN user has unlocked with a biometric auth (ie: face auth)
- // and biometric running state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
- mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
- BiometricSourceType.FACE);
- reset(mLockIconView);
- when(mLockIconView.isVisibleToUser()).thenReturn(true);
- when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(true);
-
- // WHEN the unlocked state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
- mKeyguardStateCallback.onUnlockedChanged();
-
- // THEN the lock icon is shown
- verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- }
-
- @Test
- public void lockIconAccessibility_bouncerNotAnimatingAway_viewVisible() {
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureKeyguardStateCallback();
- captureKeyguardUpdateMonitorCallback();
-
- // GIVEN user has unlocked with a biometric auth (ie: face auth)
- // and biometric running state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
- mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
- BiometricSourceType.FACE);
- reset(mLockIconView);
- when(mLockIconView.isVisibleToUser()).thenReturn(true);
- when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(false);
-
- // WHEN the unlocked state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
- mKeyguardStateCallback.onUnlockedChanged();
-
- // THEN the lock icon is shown
- verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
-
- @Test
- public void playHaptic_onTouchExploration_performHapticFeedback() {
- // WHEN request to vibrate on touch exploration
- mUnderTest.vibrateOnTouchExploration();
-
- // THEN performHapticFeedback is used
- verify(mVibrator).performHapticFeedback(any(), eq(HapticFeedbackConstants.CONTEXT_CLICK));
- }
-
- @Test
- public void playHaptic_onLongPress_performHapticFeedback() {
- // WHEN request to vibrate on long press
- mUnderTest.vibrateOnLongPress();
-
- // THEN uses perform haptic feedback
- verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
- }
-
- @Test
- public void longPress_showBouncer_sceneContainerNotEnabled() {
- init(/* useMigrationFlag= */ false);
- when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
-
- // WHEN longPress
- mUnderTest.onLongPress();
-
- // THEN show primary bouncer via keyguard view controller, not scene container
- verify(mKeyguardViewController).showPrimaryBouncer(anyBoolean());
- verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
- }
-
- @Test
- @EnableSceneContainer
- public void longPress_showBouncer() {
- init(/* useMigrationFlag= */ false);
- when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
-
- // WHEN longPress
- mUnderTest.onLongPress();
-
- // THEN show primary bouncer
- verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
- verify(mDeviceEntryInteractor).attemptDeviceEntry();
- }
-
- @Test
- @EnableSceneContainer
- public void longPress_falsingTriggered_doesNotShowBouncer() {
- init(/* useMigrationFlag= */ false);
- when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true);
-
- // WHEN longPress
- mUnderTest.onLongPress();
-
- // THEN don't show primary bouncer
- verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
- verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
deleted file mode 100644
index 2fd3cb0f0592..000000000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2022 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.keyguard
-
-import android.view.View
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.keyguard.LockIconView.ICON_LOCK
-import com.android.systemui.doze.util.getBurnInOffset
-import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.util.mockito.whenever
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.runBlocking
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class LegacyLockIconViewControllerWithCoroutinesTest : LegacyLockIconViewControllerBaseTest() {
-
- /** After migration, replaces LockIconViewControllerTest version */
- @Test
- fun testLockIcon_clearsIconWhenUnlocked() =
- runBlocking(IMMEDIATE) {
- // GIVEN udfps not enrolled
- setupUdfps()
- whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false)
-
- // GIVEN starting state for the lock icon
- setupShowLockIcon()
- whenever(mStatusBarStateController.state).thenReturn(StatusBarState.SHADE)
-
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */ true)
- reset(mLockIconView)
-
- // WHEN the dozing state changes
- mUnderTest.mIsDozingCallback.accept(false)
- // THEN the icon is cleared
- verify(mLockIconView).clearIcon()
- }
-
- /** After migration, replaces LockIconViewControllerTest version */
- @Test
- fun testLockIcon_updateToAodLock_whenUdfpsEnrolled() =
- runBlocking(IMMEDIATE) {
- // GIVEN udfps enrolled
- setupUdfps()
- whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true)
-
- // GIVEN starting state for the lock icon
- setupShowLockIcon()
-
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */ true)
- reset(mLockIconView)
-
- // WHEN the dozing state changes
- mUnderTest.mIsDozingCallback.accept(true)
-
- // THEN the AOD lock icon should show
- verify(mLockIconView).updateIcon(ICON_LOCK, true)
- }
-
- /** After migration, replaces LockIconViewControllerTest version */
- @Test
- fun testBurnInOffsetsUpdated_onDozeAmountChanged() =
- runBlocking(IMMEDIATE) {
- // GIVEN udfps enrolled
- setupUdfps()
- whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true)
-
- // GIVEN burn-in offset = 5
- val burnInOffset = 5
- whenever(getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset)
-
- // GIVEN starting state for the lock icon (keyguard)
- setupShowLockIcon()
- init(/* useMigrationFlag= */ true)
- reset(mLockIconView)
-
- // WHEN dozing updates
- mUnderTest.mIsDozingCallback.accept(true)
- mUnderTest.mDozeTransitionCallback.accept(1f)
-
- // THEN the view's translation is updated to use the AoD burn-in offsets
- verify(mLockIconView).setTranslationY(burnInOffset.toFloat())
- verify(mLockIconView).setTranslationX(burnInOffset.toFloat())
- reset(mLockIconView)
-
- // WHEN the device is no longer dozing
- mUnderTest.mIsDozingCallback.accept(false)
- mUnderTest.mDozeTransitionCallback.accept(0f)
-
- // THEN the view is updated to NO translation (no burn-in offsets anymore)
- verify(mLockIconView).setTranslationY(0f)
- verify(mLockIconView).setTranslationX(0f)
- }
-
- @Test
- fun testHideLockIconView_onLockscreenHostedDreamStateChanged() =
- runBlocking(IMMEDIATE) {
- // GIVEN starting state for the lock icon (keyguard) and wallpaper dream enabled
- mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, true)
- setupShowLockIcon()
- init(/* useMigrationFlag= */ true)
- reset(mLockIconView)
-
- // WHEN dream starts
- mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept(
- true /* isActiveDreamLockscreenHosted */
- )
-
- // THEN the lock icon is hidden
- verify(mLockIconView).visibility = View.INVISIBLE
- reset(mLockIconView)
-
- // WHEN the device is no longer dreaming
- mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept(
- false /* isActiveDreamLockscreenHosted */
- )
-
- // THEN lock icon is visible
- verify(mLockIconView).visibility = View.VISIBLE
- }
-
- companion object {
- private val IMMEDIATE = Dispatchers.Main.immediate
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
index 8b427fbc5fb8..071acfa44650 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
@@ -156,7 +156,7 @@ class TransitionAnimatorTest(val useSpring: Boolean) : SysuiTestCase() {
createEndState(transitionContainer),
backgroundLayer,
fadeWindowBackgroundLayer,
- useSpring,
+ useSpring = useSpring,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
deleted file mode 100644
index 6dc4b10a57da..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.graphics.Point
-import android.hardware.biometrics.BiometricSourceType
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
-import android.testing.TestableLooper.RunWithLooper
-import android.util.DisplayMetrics
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.keyguard.logging.KeyguardLogger
-import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
-import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor
-import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.LightRevealScrim
-import com.android.systemui.statusbar.NotificationShadeWindowController
-import com.android.systemui.statusbar.commandline.CommandRegistry
-import com.android.systemui.statusbar.phone.BiometricUnlockController
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.leak.RotationUtils
-import com.android.systemui.util.mockito.any
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import org.junit.After
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
-import org.mockito.MockitoSession
-import org.mockito.quality.Strictness
-import javax.inject.Provider
-
-
-@ExperimentalCoroutinesApi
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class AuthRippleControllerTest : SysuiTestCase() {
- private lateinit var staticMockSession: MockitoSession
-
- private lateinit var controller: AuthRippleController
- @Mock private lateinit var rippleView: AuthRippleView
- @Mock private lateinit var commandRegistry: CommandRegistry
- @Mock private lateinit var configurationController: ConfigurationController
- @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock private lateinit var authController: AuthController
- @Mock private lateinit var authRippleInteractor: AuthRippleInteractor
- @Mock private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
- @Mock
- private lateinit var notificationShadeWindowController: NotificationShadeWindowController
- @Mock
- private lateinit var biometricUnlockController: BiometricUnlockController
- @Mock
- private lateinit var udfpsControllerProvider: Provider<UdfpsController>
- @Mock
- private lateinit var udfpsController: UdfpsController
- @Mock
- private lateinit var statusBarStateController: StatusBarStateController
- @Mock
- private lateinit var lightRevealScrim: LightRevealScrim
- @Mock
- private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
-
- private val facePropertyRepository = FakeFacePropertyRepository()
- private val displayMetrics = DisplayMetrics()
-
- @Captor
- private lateinit var biometricUnlockListener:
- ArgumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener>
-
- @Before
- fun setUp() {
- mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- MockitoAnnotations.initMocks(this)
- staticMockSession = mockitoSession()
- .mockStatic(RotationUtils::class.java)
- .strictness(Strictness.LENIENT)
- .startMocking()
-
- `when`(RotationUtils.getRotation(context)).thenReturn(RotationUtils.ROTATION_NONE)
- `when`(authController.udfpsProps).thenReturn(listOf(fpSensorProp))
- `when`(udfpsControllerProvider.get()).thenReturn(udfpsController)
-
- controller = AuthRippleController(
- context,
- authController,
- configurationController,
- keyguardUpdateMonitor,
- keyguardStateController,
- wakefulnessLifecycle,
- commandRegistry,
- notificationShadeWindowController,
- udfpsControllerProvider,
- statusBarStateController,
- displayMetrics,
- KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)),
- biometricUnlockController,
- lightRevealScrim,
- authRippleInteractor,
- facePropertyRepository,
- rippleView,
- )
- controller.init()
- }
-
- @After
- fun tearDown() {
- staticMockSession.finishMocking()
- }
-
- @Test
- fun testFingerprintTrigger_KeyguardShowing_Ripple() {
- // GIVEN fp exists, keyguard is showing, unlocking with fp allowed
- val fpsLocation = Point(5, 5)
- `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
- controller.onViewAttached()
- `when`(keyguardStateController.isShowing).thenReturn(true)
- `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
- eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
-
- // WHEN fingerprint authenticated
- verify(biometricUnlockController).addListener(biometricUnlockListener.capture())
- biometricUnlockListener.value
- .onBiometricUnlockedWithKeyguardDismissal(BiometricSourceType.FINGERPRINT)
-
- // THEN update sensor location and show ripple
- verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
- verify(rippleView).startUnlockedRipple(any())
- }
-
- @Test
- fun testFingerprintTrigger_KeyguardNotShowing_NoRipple() {
- // GIVEN fp exists & unlocking with fp allowed
- val fpsLocation = Point(5, 5)
- `when`(authController.udfpsLocation).thenReturn(fpsLocation)
- controller.onViewAttached()
- `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
- eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
-
- // WHEN keyguard is NOT showing & fingerprint authenticated
- `when`(keyguardStateController.isShowing).thenReturn(false)
- val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
- captor.value.onBiometricAuthenticated(
- 0 /* userId */,
- BiometricSourceType.FINGERPRINT /* type */,
- false /* isStrongBiometric */)
-
- // THEN no ripple
- verify(rippleView, never()).startUnlockedRipple(any())
- }
-
- @Test
- fun testFingerprintTrigger_biometricUnlockNotAllowed_NoRipple() {
- // GIVEN fp exists & keyguard is showing
- val fpsLocation = Point(5, 5)
- `when`(authController.udfpsLocation).thenReturn(fpsLocation)
- controller.onViewAttached()
- `when`(keyguardStateController.isShowing).thenReturn(true)
-
- // WHEN unlocking with fingerprint is NOT allowed & fingerprint authenticated
- `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
- eq(BiometricSourceType.FINGERPRINT))).thenReturn(false)
- val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
- captor.value.onBiometricAuthenticated(
- 0 /* userId */,
- BiometricSourceType.FINGERPRINT /* type */,
- false /* isStrongBiometric */)
-
- // THEN no ripple
- verify(rippleView, never()).startUnlockedRipple(any())
- }
-
- @Test
- fun testNullFaceSensorLocationDoesNothing() {
- facePropertyRepository.setSensorLocation(null)
- controller.onViewAttached()
-
- val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-
- captor.value.onBiometricAuthenticated(
- 0 /* userId */,
- BiometricSourceType.FACE /* type */,
- false /* isStrongBiometric */)
- verify(rippleView, never()).startUnlockedRipple(any())
- }
-
- @Test
- fun testNullFingerprintSensorLocationDoesNothing() {
- `when`(authController.fingerprintSensorLocation).thenReturn(null)
- controller.onViewAttached()
-
- val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-
- captor.value.onBiometricAuthenticated(
- 0 /* userId */,
- BiometricSourceType.FINGERPRINT /* type */,
- false /* isStrongBiometric */)
- verify(rippleView, never()).startUnlockedRipple(any())
- }
-
- @Test
- fun registersAndDeregisters() {
- controller.onViewAttached()
- val captor = ArgumentCaptor
- .forClass(KeyguardStateController.Callback::class.java)
- verify(keyguardStateController).addCallback(captor.capture())
- val captor2 = ArgumentCaptor
- .forClass(WakefulnessLifecycle.Observer::class.java)
- verify(wakefulnessLifecycle).addObserver(captor2.capture())
- controller.onViewDetached()
- verify(keyguardStateController).removeCallback(any())
- verify(wakefulnessLifecycle).removeObserver(any())
- }
-
- @Test
- @RunWithLooper(setAsMainLooper = true)
- fun testAnimatorRunWhenWakeAndUnlock_fingerprint() {
- mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
- val fpsLocation = Point(5, 5)
- `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
- controller.onViewAttached()
- `when`(keyguardStateController.isShowing).thenReturn(true)
- `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
- BiometricSourceType.FINGERPRINT)).thenReturn(true)
- `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
-
- controller.showUnlockRipple(BiometricSourceType.FINGERPRINT)
- assertTrue("reveal didn't start on keyguardFadingAway",
- controller.startLightRevealScrimOnKeyguardFadingAway)
- `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
- controller.onKeyguardFadingAwayChanged()
- assertFalse("reveal triggers multiple times",
- controller.startLightRevealScrimOnKeyguardFadingAway)
- }
-
- @Test
- @RunWithLooper(setAsMainLooper = true)
- fun testAnimatorRunWhenWakeAndUnlock_faceUdfpsFingerDown() {
- mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
- val faceLocation = Point(5, 5)
- facePropertyRepository.setSensorLocation(faceLocation)
- controller.onViewAttached()
- `when`(keyguardStateController.isShowing).thenReturn(true)
- `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
- `when`(authController.isUdfpsFingerDown).thenReturn(true)
- `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
- eq(BiometricSourceType.FACE))).thenReturn(true)
-
- controller.showUnlockRipple(BiometricSourceType.FACE)
- assertTrue("reveal didn't start on keyguardFadingAway",
- controller.startLightRevealScrimOnKeyguardFadingAway)
- `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
- controller.onKeyguardFadingAwayChanged()
- assertFalse("reveal triggers multiple times",
- controller.startLightRevealScrimOnKeyguardFadingAway)
- }
-
- @Test
- fun testUpdateRippleColor() {
- controller.onViewAttached()
- val captor = ArgumentCaptor
- .forClass(ConfigurationController.ConfigurationListener::class.java)
- verify(configurationController).addCallback(captor.capture())
-
- reset(rippleView)
- captor.value.onThemeChanged()
- verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
-
- reset(rippleView)
- captor.value.onUiModeChanged()
- verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
- }
-
- @Test
- fun testUdfps_onFingerDown_runningForDeviceEntry_showDwellRipple() {
- // GIVEN fingerprint detection is running on keyguard
- `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(true)
-
- // GIVEN view is already attached
- controller.onViewAttached()
- val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java)
- verify(udfpsController).addCallback(captor.capture())
-
- // GIVEN fp is updated to Point(5, 5)
- val fpsLocation = Point(5, 5)
- `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
-
- // WHEN finger is down
- captor.value.onFingerDown()
-
- // THEN update sensor location and show ripple
- verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
- verify(rippleView).startDwellRipple(false)
- }
-
- @Test
- fun testUdfps_onFingerDown_notDeviceEntry_doesNotShowDwellRipple() {
- // GIVEN fingerprint detection is NOT running on keyguard
- `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(false)
-
- // GIVEN view is already attached
- controller.onViewAttached()
- val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java)
- verify(udfpsController).addCallback(captor.capture())
-
- // WHEN finger is down
- captor.value.onFingerDown()
-
- // THEN doesn't show dwell ripple
- verify(rippleView, never()).startDwellRipple(false)
- }
-}
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 e2a6a5508992..4baca713e19f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -16,21 +16,14 @@
package com.android.systemui.biometrics
-import android.graphics.Rect
import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP
import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING
import android.hardware.biometrics.BiometricRequestConstants.RequestReason
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
import android.testing.TestableLooper.RunWithLooper
import android.view.LayoutInflater
import android.view.MotionEvent
-import android.view.Surface
-import android.view.Surface.Rotation
import android.view.View
-import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -38,7 +31,6 @@ import androidx.test.filters.SmallTest
import com.android.app.viewcapture.ViewCapture
import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
@@ -61,9 +53,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
-import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
@@ -86,7 +76,6 @@ import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
-import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -118,7 +107,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var dialogManager: SystemUIDialogManager
@Mock private lateinit var dumpManager: DumpManager
- @Mock private lateinit var transitionController: LockscreenShadeTransitionController
@Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock
@@ -126,8 +114,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider
@Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
@Mock private lateinit var udfpsController: UdfpsController
- @Mock private lateinit var udfpsView: UdfpsView
- @Mock private lateinit var mUdfpsKeyguardViewLegacy: UdfpsKeyguardViewLegacy
@Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator
@Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
@Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@@ -147,7 +133,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
private lateinit var powerInteractor: PowerInteractor
private lateinit var testScope: TestScope
- private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
+ private val onTouch = { _: View, _: MotionEvent -> true }
private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
private lateinit var controllerOverlay: UdfpsControllerOverlay
@@ -158,53 +144,37 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
powerInteractor = kosmos.powerInteractor
keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
- whenever(inflater.inflate(R.layout.udfps_view, null, false)).thenReturn(udfpsView)
- whenever(inflater.inflate(R.layout.udfps_bp_view, null))
- .thenReturn(mock(UdfpsBpView::class.java))
- whenever(inflater.inflate(R.layout.udfps_keyguard_view_legacy, null))
- .thenReturn(mUdfpsKeyguardViewLegacy)
- whenever(inflater.inflate(R.layout.udfps_fpm_empty_view, null))
- .thenReturn(mock(UdfpsFpmEmptyView::class.java))
}
private suspend fun withReasonSuspend(
@RequestReason reason: Int,
isDebuggable: Boolean = false,
- enableDeviceEntryUdfpsRefactor: Boolean = false,
block: suspend () -> Unit,
) {
- withReason(
- reason,
- isDebuggable,
- enableDeviceEntryUdfpsRefactor,
- )
+ withReason(reason, isDebuggable)
block()
}
private fun withReason(
@RequestReason reason: Int,
isDebuggable: Boolean = false,
- enableDeviceEntryUdfpsRefactor: Boolean = false,
block: () -> Unit = {},
) {
- if (enableDeviceEntryUdfpsRefactor) {
- mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- } else {
- mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- }
controllerOverlay =
UdfpsControllerOverlay(
context,
inflater,
- ViewCaptureAwareWindowManager(windowManager, lazyViewCapture,
- isViewCaptureEnabled = false),
+ ViewCaptureAwareWindowManager(
+ windowManager,
+ lazyViewCapture,
+ isViewCaptureEnabled = false,
+ ),
accessibilityManager,
statusBarStateController,
statusBarKeyguardViewManager,
keyguardUpdateMonitor,
dialogManager,
dumpManager,
- transitionController,
configurationController,
keyguardStateController,
unlockedScreenOffAnimationController,
@@ -230,117 +200,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
block()
}
- @Test fun showUdfpsOverlay_bp() = withReason(REASON_AUTH_BP) { showUdfpsOverlay() }
-
- @Test
- fun showUdfpsOverlay_keyguard() =
- withReason(REASON_AUTH_KEYGUARD) {
- showUdfpsOverlay()
- verify(mUdfpsKeyguardViewLegacy).updateSensorLocation(eq(overlayParams.sensorBounds))
- }
-
- @Test fun showUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { showUdfpsOverlay() }
-
- private fun withRotation(@Rotation rotation: Int, block: () -> Unit) {
- // Sensor that's in the top left corner of the display in natural orientation.
- val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT)
- val overlayBounds = Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
- overlayParams =
- UdfpsOverlayParams(
- sensorBounds,
- overlayBounds,
- DISPLAY_WIDTH,
- DISPLAY_HEIGHT,
- scaleFactor = 1f,
- rotation
- )
- block()
- }
-
- @Test
- fun showUdfpsOverlay_withRotation0() =
- withRotation(Surface.ROTATION_0) {
- withReason(REASON_AUTH_BP) {
- controllerOverlay.show(udfpsController, overlayParams)
- verify(windowManager)
- .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
- // ROTATION_0 is the native orientation. Sensor should stay in the top left corner.
- val lp = layoutParamsCaptor.value
- assertThat(lp.x).isEqualTo(0)
- assertThat(lp.y).isEqualTo(0)
- assertThat(lp.width).isEqualTo(DISPLAY_WIDTH)
- assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT)
- }
- }
-
- @Test
- fun showUdfpsOverlay_withRotation180() =
- withRotation(Surface.ROTATION_180) {
- withReason(REASON_AUTH_BP) {
- controllerOverlay.show(udfpsController, overlayParams)
- verify(windowManager)
- .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
- // ROTATION_180 is not supported. Sensor should stay in the top left corner.
- val lp = layoutParamsCaptor.value
- assertThat(lp.x).isEqualTo(0)
- assertThat(lp.y).isEqualTo(0)
- assertThat(lp.width).isEqualTo(DISPLAY_WIDTH)
- assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT)
- }
- }
-
- @Test
- fun showUdfpsOverlay_withRotation90() =
- withRotation(Surface.ROTATION_90) {
- withReason(REASON_AUTH_BP) {
- controllerOverlay.show(udfpsController, overlayParams)
- verify(windowManager)
- .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
- // Sensor should be in the bottom left corner in ROTATION_90.
- val lp = layoutParamsCaptor.value
- assertThat(lp.x).isEqualTo(0)
- assertThat(lp.y).isEqualTo(0)
- assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT)
- assertThat(lp.height).isEqualTo(DISPLAY_WIDTH)
- }
- }
-
- @Test
- fun showUdfpsOverlay_withRotation270() =
- withRotation(Surface.ROTATION_270) {
- withReason(REASON_AUTH_BP) {
- controllerOverlay.show(udfpsController, overlayParams)
- verify(windowManager)
- .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
- // Sensor should be in the top right corner in ROTATION_270.
- val lp = layoutParamsCaptor.value
- assertThat(lp.x).isEqualTo(0)
- assertThat(lp.y).isEqualTo(0)
- assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT)
- assertThat(lp.height).isEqualTo(DISPLAY_WIDTH)
- }
- }
-
- @Test
- fun showUdfpsOverlay_awake() =
- testScope.runTest {
- withReason(REASON_AUTH_KEYGUARD) {
- powerRepository.updateWakefulness(
- rawState = WakefulnessState.AWAKE,
- lastWakeReason = WakeSleepReason.POWER_BUTTON,
- lastSleepReason = WakeSleepReason.OTHER,
- )
- runCurrent()
- controllerOverlay.show(udfpsController, overlayParams)
- runCurrent()
- verify(windowManager).addView(any(), any())
- }
- }
-
@Test
fun showUdfpsOverlay_whileGoingToSleep() =
testScope.runTest {
@@ -412,91 +271,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
}
@Test
- fun showUdfpsOverlay_afterFinishedTransitioningToAOD() =
- testScope.runTest {
- withReasonSuspend(REASON_AUTH_KEYGUARD) {
- keyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.OFF,
- to = KeyguardState.GONE,
- testScope = this,
- )
- powerRepository.updateWakefulness(
- rawState = WakefulnessState.STARTING_TO_SLEEP,
- lastWakeReason = WakeSleepReason.POWER_BUTTON,
- lastSleepReason = WakeSleepReason.OTHER,
- )
- runCurrent()
-
- // WHEN a request comes to show the view
- controllerOverlay.show(udfpsController, overlayParams)
- runCurrent()
-
- // THEN the view does not get added immediately
- verify(windowManager, never()).addView(any(), any())
-
- // WHEN the device finishes transitioning to AOD
- keyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- testScope = this,
- )
- runCurrent()
-
- // THEN the view gets added
- verify(windowManager)
- .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
- }
- }
-
- private fun showUdfpsOverlay() {
- val didShow = controllerOverlay.show(udfpsController, overlayParams)
-
- verify(windowManager).addView(eq(controllerOverlay.getTouchOverlay()), any())
- verify(udfpsView).setUdfpsDisplayModeProvider(eq(udfpsDisplayMode))
- verify(udfpsView).animationViewController = any()
- verify(udfpsView).addView(any())
-
- assertThat(didShow).isTrue()
- assertThat(controllerOverlay.isShowing).isTrue()
- assertThat(controllerOverlay.isHiding).isFalse()
- assertThat(controllerOverlay.getTouchOverlay()).isNotNull()
- }
-
- @Test fun hideUdfpsOverlay_bp() = withReason(REASON_AUTH_BP) { hideUdfpsOverlay() }
-
- @Test fun hideUdfpsOverlay_keyguard() = withReason(REASON_AUTH_KEYGUARD) { hideUdfpsOverlay() }
-
- @Test fun hideUdfpsOverlay_settings() = withReason(REASON_AUTH_SETTINGS) { hideUdfpsOverlay() }
-
- @Test fun hideUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { hideUdfpsOverlay() }
-
- private fun hideUdfpsOverlay() {
- val didShow = controllerOverlay.show(udfpsController, overlayParams)
- val view = controllerOverlay.getTouchOverlay()
- view?.let { whenever(view.parent).thenReturn(mock(ViewGroup::class.java)) }
- val didHide = controllerOverlay.hide()
-
- verify(windowManager).removeView(eq(view))
-
- assertThat(didShow).isTrue()
- assertThat(didHide).isTrue()
- assertThat(controllerOverlay.getTouchOverlay()).isNull()
- assertThat(controllerOverlay.animationViewController).isNull()
- assertThat(controllerOverlay.isShowing).isFalse()
- assertThat(controllerOverlay.isHiding).isTrue()
- }
-
- @Test
fun canNotHide() = withReason(REASON_AUTH_BP) { assertThat(controllerOverlay.hide()).isFalse() }
@Test
- fun canNotReshow() =
- withReason(REASON_AUTH_BP) {
- assertThat(controllerOverlay.show(udfpsController, overlayParams)).isTrue()
- assertThat(controllerOverlay.show(udfpsController, overlayParams)).isFalse()
- }
-
- @Test
fun cancels() =
withReason(REASON_AUTH_BP) {
controllerOverlay.cancel()
@@ -504,16 +281,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
}
@Test
- fun unconfigureDisplayOnHide() =
- withReason(REASON_AUTH_BP) {
- whenever(udfpsView.isDisplayConfigured).thenReturn(true)
-
- controllerOverlay.show(udfpsController, overlayParams)
- controllerOverlay.hide()
- verify(udfpsView).unconfigureDisplay()
- }
-
- @Test
fun matchesRequestIds() =
withReason(REASON_AUTH_BP) {
assertThat(controllerOverlay.matchesRequestId(REQUEST_ID)).isTrue()
@@ -521,29 +288,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
}
@Test
- fun smallOverlayOnEnrollmentWithA11y() =
- withRotation(Surface.ROTATION_0) {
- withReason(REASON_ENROLL_ENROLLING) {
- // When a11y enabled during enrollment
- whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(true)
-
- controllerOverlay.show(udfpsController, overlayParams)
- verify(windowManager)
- .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
-
- // Layout params should use sensor bounds
- val lp = layoutParamsCaptor.value
- assertThat(lp.width).isEqualTo(overlayParams.sensorBounds.width())
- assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height())
- }
- }
-
- @Test
fun addViewPending_layoutIsNotUpdated() =
testScope.runTest {
withReasonSuspend(REASON_AUTH_KEYGUARD) {
- mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
-
// GIVEN going to sleep
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.OFF,
@@ -574,26 +321,4 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
controllerOverlay.hide()
}
}
-
- @Test
- fun updateOverlayParams_viewLayoutUpdated() =
- testScope.runTest {
- withReasonSuspend(REASON_AUTH_KEYGUARD) {
- powerRepository.updateWakefulness(
- rawState = WakefulnessState.AWAKE,
- lastWakeReason = WakeSleepReason.POWER_BUTTON,
- lastSleepReason = WakeSleepReason.OTHER,
- )
- runCurrent()
- controllerOverlay.show(udfpsController, overlayParams)
- runCurrent()
- verify(windowManager).addView(any(), any())
-
- // WHEN updateOverlayParams gets called
- controllerOverlay.updateOverlayParams(overlayParams)
-
- // THEN the view layout is updated
- verify(windowManager).updateViewLayout(any(), any())
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
deleted file mode 100644
index 9fbe09619ff1..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.hardware.biometrics.SensorLocationInternal
-import android.testing.TestableLooper
-import android.testing.ViewUtils
-import android.view.LayoutInflater
-import android.view.Surface
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.withArgCaptor
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.nullable
-import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-
-private const val SENSOR_X = 50
-private const val SENSOR_Y = 250
-private const val SENSOR_RADIUS = 10
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
-class UdfpsViewTest : SysuiTestCase() {
-
- @JvmField @Rule
- var rule = MockitoJUnit.rule()
-
- @Mock
- lateinit var hbmProvider: UdfpsDisplayModeProvider
- @Mock
- lateinit var animationViewController: UdfpsAnimationViewController<UdfpsAnimationView>
-
- private lateinit var view: UdfpsView
-
- @Before
- fun setup() {
- context.setTheme(androidx.appcompat.R.style.Theme_AppCompat)
- view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView
- view.animationViewController = animationViewController
- val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect
- view.overlayParams = UdfpsOverlayParams(sensorBounds, sensorBounds, 1920,
- 1080, 1f, Surface.ROTATION_0)
- view.setUdfpsDisplayModeProvider(hbmProvider)
- ViewUtils.attachView(view)
- }
-
- @After
- fun cleanup() {
- ViewUtils.detachView(view)
- }
-
- // TODO: Add test to verify view is size of screen
-
- @Test
- fun startAndStopIllumination() {
- val onDone: Runnable = mock()
- view.configureDisplay(onDone)
-
- val illuminator = withArgCaptor<Runnable> {
- verify(hbmProvider).enable(capture())
- }
-
- assertThat(view.isDisplayConfigured).isTrue()
- verify(animationViewController).onDisplayConfiguring()
- verify(animationViewController, never()).onDisplayUnconfigured()
- verify(onDone, never()).run()
-
- // fake illumination event
- illuminator.run()
- waitForLooper()
- verify(onDone).run()
- verify(hbmProvider, never()).disable(any())
-
- view.unconfigureDisplay()
- assertThat(view.isDisplayConfigured).isFalse()
- verify(animationViewController).onDisplayUnconfigured()
- verify(hbmProvider).disable(nullable(Runnable::class.java))
- }
-
- private fun waitForLooper() = TestableLooper.get(this).processAllMessages()
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index b87647ef9839..eb72f29046aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -48,11 +48,11 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.res.Configuration;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.testing.AndroidTestingRunner;
-import android.testing.UiThreadTest;
+import androidx.test.annotation.UiThreadTest;
import android.view.Display;
import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -69,7 +69,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@UiThreadTest
public class DozeMachineTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
index ae2a9add4fb7..6fce108f35a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest_311121830.kt
@@ -20,10 +20,10 @@ import android.animation.AnimatorTestRule
import android.content.Context
import android.service.quicksettings.Tile
import android.testing.AndroidTestingRunner
-import android.testing.UiThreadTest
import android.view.ContextThemeWrapper
import android.view.View
import android.widget.ImageView
+import androidx.test.annotation.UiThreadTest
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.qs.QSTile
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
index 643debfbc810..7b24233d8603 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
@@ -17,13 +17,13 @@ import android.os.Handler;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.testing.TestableLooper;
-import android.testing.UiThreadTest;
import android.view.View;
import android.view.Window;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;
+import androidx.test.annotation.UiThreadTest;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt
deleted file mode 100644
index 0d1d37af7e5b..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.systemui.statusbar.core
-
-import android.platform.test.annotations.EnableFlags
-import android.view.Display
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.display.data.repository.displayRepository
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.kosmos.unconfinedTestDispatcher
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
-class MultiDisplayStatusBarInitializerStoreTest : SysuiTestCase() {
-
- private val kosmos =
- testKosmos().also {
- // Using unconfinedTestDispatcher to avoid having to call `runCurrent` in the tests.
- it.testDispatcher = it.unconfinedTestDispatcher
- }
- private val testScope = kosmos.testScope
- private val fakeDisplayRepository = kosmos.displayRepository
- private val store = kosmos.multiDisplayStatusBarInitializerStore
-
- @Before
- fun start() {
- store.start()
- }
-
- @Before
- fun addDisplays() = runBlocking {
- fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY_ID)
- fakeDisplayRepository.addDisplay(NON_DEFAULT_DISPLAY_ID)
- }
-
- @Test
- fun forDisplay_defaultDisplay_multipleCalls_returnsSameInstance() =
- testScope.runTest {
- val controller = store.defaultDisplay
-
- assertThat(store.defaultDisplay).isSameInstanceAs(controller)
- }
-
- @Test
- fun forDisplay_nonDefaultDisplay_multipleCalls_returnsSameInstance() =
- testScope.runTest {
- val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
-
- assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isSameInstanceAs(controller)
- }
-
- @Test
- fun forDisplay_nonDefaultDisplay_afterDisplayRemoved_returnsNewInstance() =
- testScope.runTest {
- val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID)
-
- fakeDisplayRepository.removeDisplay(NON_DEFAULT_DISPLAY_ID)
- fakeDisplayRepository.addDisplay(NON_DEFAULT_DISPLAY_ID)
-
- assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(controller)
- }
-
- @Test(expected = IllegalArgumentException::class)
- fun forDisplay_nonExistingDisplayId_throws() =
- testScope.runTest { store.forDisplay(NON_EXISTING_DISPLAY_ID) }
-
- companion object {
- private const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY
- private const val NON_DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1
- private const val NON_EXISTING_DISPLAY_ID = Display.DEFAULT_DISPLAY + 2
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
index 2d8e69280d30..c8ef663a4f81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java
@@ -31,12 +31,12 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.testing.AndroidTestingRunner;
-import android.testing.UiThreadTest;
import android.util.FloatProperty;
import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import com.android.app.animation.Interpolators;
@@ -270,4 +270,4 @@ public class PropertyAnimatorTest extends SysuiTestCase {
PropertyAnimator.startAnimation(mView, mProperty, 200f, mAnimationProperties);
assertTrue(PropertyAnimator.isAnimating(mView, mProperty));
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index ed7383cacd29..e3e2491e5d52 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -612,6 +612,8 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
Assert.assertNull(child.getParent());
Assert.assertNull(child.getNotificationParent());
Assert.assertFalse(child.keepInParentForDismissAnimation());
+ verify(mNotificationTestHelper.getMockLogger())
+ .logCancelAppearDrawing(child.getEntry(), false);
verifyNoMoreInteractions(mNotificationTestHelper.getMockLogger());
}
@@ -1013,7 +1015,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
assertThat(row.isHeadsUpAnimatingAway()).isTrue();
// on disappear animation ends
- row.onAppearAnimationFinished(/* wasAppearing = */ false);
+ row.onAppearAnimationFinished(/* wasAppearing = */ false, /* cancelled = */ false);
assertThat(row.isHeadsUpAnimatingAway()).isFalse();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 40b8cdafa813..8f64287ebd0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -21,7 +21,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
import static android.provider.Settings.Global.HEADS_UP_ON;
-import static com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR;
import static com.android.systemui.Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE;
import static com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION;
import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
@@ -513,10 +512,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mLightBarController,
mAutoHideController,
new StatusBarInitializerImpl(
- mContext.getDisplayId(),
- mStatusBarWindowControllerStore,
+ mStatusBarWindowController,
mCollapsedStatusBarFragmentProvider,
- emptySet()),
+ emptySet()
+ ),
mStatusBarWindowControllerStore,
mStatusBarWindowStateController,
new FakeStatusBarModeRepository(),
@@ -859,34 +858,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
@Test
- @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void testSetDozingNotUnlocking_transitionToAuthScrimmed_cancelKeyguardFadingAway() {
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
- when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
-
- mCentralSurfaces.updateScrimController();
-
- verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
- verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway();
- }
-
- @Test
- @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void testOccludingQSNotExpanded_flagOff_transitionToAuthScrimmed() {
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
- // GIVEN device occluded and panel is NOT expanded
- mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
- when(mKeyguardStateController.isOccluded()).thenReturn(true);
- when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(false);
-
- mCentralSurfaces.updateScrimController();
-
- verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED));
- }
-
- @Test
- @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
public void testNotOccluding_QSNotExpanded_flagOn_doesNotTransitionScrimState() {
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
@@ -902,7 +873,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
@Test
- @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
public void testNotOccluding_QSExpanded_flagOn_doesTransitionScrimStateToKeyguard() {
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
@@ -918,21 +888,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
@Test
- @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- public void testOccludingQSExpanded_transitionToAuthScrimmedShade() {
- when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
-
- // GIVEN device occluded and qs IS expanded
- mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
- when(mKeyguardStateController.isOccluded()).thenReturn(true);
- when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true);
-
- mCentralSurfaces.updateScrimController();
-
- verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
- }
-
- @Test
public void testEnteringGlanceableHub_updatesScrim() {
// Transition to the glanceable hub.
mKosmos.getCommunalRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index e804b33db1f7..eecf36e9343b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -309,8 +309,6 @@ public class ScrimControllerTest extends SysuiTestCase {
// Attach behind scrim so flows that are collecting on it start running.
ViewUtils.attachView(mScrimBehind);
- mScrimController.setHasBackdrop(false);
-
mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
mTestScope.getTestScheduler().runCurrent();
@@ -483,47 +481,6 @@ public class ScrimControllerTest extends SysuiTestCase {
}
@Test
- public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() {
- mScrimController.setHasBackdrop(true);
- mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
- mTestScope.getTestScheduler().runCurrent();
-
- mScrimController.legacyTransitionTo(ScrimState.AOD);
- finishAnimationsImmediately();
-
- assertScrimAlpha(Map.of(
- mScrimInFront, TRANSPARENT,
- mScrimBehind, TRANSPARENT));
- assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
-
- assertScrimTinted(Map.of(
- mScrimInFront, true,
- mScrimBehind, true
- ));
- }
-
- @Test
- public void setHasBackdrop_withAodWallpaperAndAlbumArt() {
- mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
- mTestScope.getTestScheduler().runCurrent();
-
- mScrimController.legacyTransitionTo(ScrimState.AOD);
- finishAnimationsImmediately();
- mScrimController.setHasBackdrop(true);
- finishAnimationsImmediately();
-
- assertScrimAlpha(Map.of(
- mScrimInFront, TRANSPARENT,
- mScrimBehind, TRANSPARENT));
- assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
-
- assertScrimTinted(Map.of(
- mScrimInFront, true,
- mScrimBehind, true
- ));
- }
-
- @Test
public void transitionToAod_withFrontAlphaUpdates() {
// Assert that setting the AOD front scrim alpha doesn't take effect in a non-AOD state.
mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
@@ -1356,7 +1313,6 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
- mScrimController.setHasBackdrop(false);
mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
mTestScope.getTestScheduler().runCurrent();
mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index e57e8d108529..15ef917343ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone.fragment;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS;
import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS;
import static com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT;
import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
@@ -61,6 +60,7 @@ import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.OperatorNameViewController;
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
@@ -633,7 +633,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags({
FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS,
- FLAG_STATUS_BAR_RON_CHIPS,
+ StatusBarNotifChips.FLAG_NAME,
FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
public void hasPrimaryOngoingActivity_viewsUnchangedWhenSimpleFragmentFlagOn() {
resumeAndGetFragment();
@@ -660,8 +660,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
- public void hasSecondaryOngoingActivity_butRonsFlagOff_secondaryChipHidden() {
+ @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() {
resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -673,7 +673,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() {
resumeAndGetFragment();
@@ -689,8 +689,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
- public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_ronsFlagOff() {
+ @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -705,9 +705,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
- public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_ronsFlagOn() {
+ public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -724,8 +724,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
- public void hasOngoingActivityButAlsoHun_chipHidden_ronsFlagOff() {
+ @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -740,9 +740,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
- public void hasOngoingActivitiesButAlsoHun_chipsHidden_ronsFlagOn() {
+ public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
@@ -759,8 +759,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
- public void primaryOngoingActivityEnded_chipHidden_ronsFlagOff() {
+ @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() {
resumeAndGetFragment();
// Ongoing activity started
@@ -781,9 +781,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
- public void primaryOngoingActivityEnded_chipHidden_ronsFlagOn() {
+ public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() {
resumeAndGetFragment();
// Ongoing activity started
@@ -804,7 +804,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
public void secondaryOngoingActivityEnded_chipHidden() {
resumeAndGetFragment();
@@ -828,8 +828,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
- public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOff() {
+ @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// Enable animations for testing so that we can verify we still aren't animating
fragment.enableAnimationsForTesting();
@@ -846,9 +846,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
- public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOn() {
+ public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// Enable animations for testing so that we can verify we still aren't animating
fragment.enableAnimationsForTesting();
@@ -866,8 +866,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Test
@EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
- @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
- public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOff() {
+ @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT})
+ public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN there *is* an ongoing call via old callback
@@ -898,9 +898,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
}
@Test
- @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS})
+ @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME})
@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT)
- public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOn() {
+ public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN there *is* an ongoing call via old callback
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 5996ef13a463..1ceb20adeebd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -198,6 +198,8 @@ import com.android.wm.shell.taskview.TaskView;
import com.android.wm.shell.taskview.TaskViewTransitions;
import com.android.wm.shell.transition.Transitions;
+import kotlin.Lazy;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -216,7 +218,6 @@ import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executor;
-import kotlin.Lazy;
import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
import platform.test.runner.parameterized.Parameters;
@@ -2481,7 +2482,7 @@ public class BubblesTest extends SysuiTestCase {
mEntryListener.onEntryAdded(mRow);
- verify(mBubbleLogger).log(argThat(b -> b.getKey().equals(mRow.getKey())),
+ verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED));
}
@@ -2498,10 +2499,25 @@ public class BubblesTest extends SysuiTestCase {
NotificationEntryHelper.modifyRanking(mRow).setTextChanged(true).build();
mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
- verify(mBubbleLogger).log(argThat(b -> b.getKey().equals(mRow.getKey())),
+ verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_UPDATED));
}
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void testEventLogging_bubbleBar_dragBubbleToDismiss() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ mEntryListener.onEntryAdded(mRow);
+ mBubbleController.dragBubbleToDismiss(mRow.getKey(), 1L);
+
+ verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()),
+ eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE));
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
@@ -2687,6 +2703,10 @@ public class BubblesTest extends SysuiTestCase {
assertThat(mSysUiStateBubblesManageMenuExpanded).isEqualTo(manageMenuExpanded);
}
+ private Bubble eqBubbleWithKey(String key) {
+ return argThat(b -> b.getKey().equals(key));
+ }
+
private static class FakeBubbleStateListener implements Bubbles.BubbleStateListener {
int mStateChangeCalls = 0;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
index baaf60447cf9..703e2d1db200 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
@@ -49,8 +49,6 @@ class FakeKeyguardBouncerRepository @Inject constructor() : KeyguardBouncerRepos
private val _isAlternateBouncerVisible = MutableStateFlow(false)
override val alternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
override var lastAlternateBouncerVisibleTime: Long = 0L
- private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false)
- override val alternateBouncerUIAvailable = _isAlternateBouncerUIAvailable.asStateFlow()
override val lastShownSecurityMode: MutableStateFlow<KeyguardSecurityModel.SecurityMode> =
MutableStateFlow(KeyguardSecurityModel.SecurityMode.Invalid)
override var bouncerDismissActionModel: BouncerDismissActionModel? = null
@@ -63,10 +61,6 @@ class FakeKeyguardBouncerRepository @Inject constructor() : KeyguardBouncerRepos
_isAlternateBouncerVisible.value = isVisible
}
- override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
- _isAlternateBouncerUIAvailable.value = isAvailable
- }
-
override fun setPrimaryShow(isShowing: Boolean) {
_primaryBouncerShow.value = isShowing
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
index 63323b239a78..8bbb8a0d320e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt
@@ -16,33 +16,23 @@
package com.android.systemui.bouncer.domain.interactor
-import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.statusbar.policy.keyguardStateController
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.systemClock
val Kosmos.alternateBouncerInteractor: AlternateBouncerInteractor by
Kosmos.Fixture {
AlternateBouncerInteractor(
- statusBarStateController = statusBarStateController,
- keyguardStateController = keyguardStateController,
bouncerRepository = keyguardBouncerRepository,
fingerprintPropertyRepository = fingerprintPropertyRepository,
- biometricSettingsRepository = biometricSettingsRepository,
systemClock = systemClock,
- keyguardUpdateMonitor = keyguardUpdateMonitor,
deviceEntryBiometricsAllowedInteractor = { deviceEntryBiometricsAllowedInteractor },
keyguardInteractor = { keyguardInteractor },
keyguardTransitionInteractor = { keyguardTransitionInteractor },
@@ -54,21 +44,9 @@ val Kosmos.alternateBouncerInteractor: AlternateBouncerInteractor by
fun Kosmos.givenCanShowAlternateBouncer() {
this.givenAlternateBouncerSupported()
this.keyguardBouncerRepository.setPrimaryShow(false)
- this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
- this.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
this.deviceEntryFaceAuthRepository.setLockedOut(false)
- whenever(this.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
- whenever(this.keyguardStateController.isUnlocked).thenReturn(false)
}
fun Kosmos.givenAlternateBouncerSupported() {
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- this.fingerprintPropertyRepository.supportsUdfps()
- } else {
- this.keyguardBouncerRepository.setAlternateBouncerUIAvailable(true)
- }
-}
-
-fun Kosmos.givenCannotShowAlternateBouncer() {
- this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+ this.fingerprintPropertyRepository.supportsUdfps()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
index 3087d01a2479..77ec83871016 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
@@ -23,6 +23,7 @@ import com.android.internal.logging.metricsLogger
import com.android.internal.util.emergencyAffordanceManager
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.bouncer.data.repository.emergencyServicesRepository
+import com.android.systemui.haptics.msdl.bouncerHapticPlayer
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
@@ -52,5 +53,6 @@ val Kosmos.bouncerActionButtonInteractor by Fixture {
metricsLogger = metricsLogger,
dozeLogger = mock(),
sceneInteractor = { sceneInteractor },
+ bouncerHapticPlayer,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
new file mode 100644
index 000000000000..e3797260ed6d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.systemui.display.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import kotlinx.coroutines.CoroutineScope
+
+class FakePerDisplayStore(
+ backgroundApplicationScope: CoroutineScope,
+ displayRepository: DisplayRepository,
+) : PerDisplayStoreImpl<TestPerDisplayInstance>(backgroundApplicationScope, displayRepository) {
+
+ val removalActions = mutableListOf<TestPerDisplayInstance>()
+
+ override fun createInstanceForDisplay(displayId: Int): TestPerDisplayInstance {
+ return TestPerDisplayInstance(displayId)
+ }
+
+ override val instanceClass = TestPerDisplayInstance::class.java
+
+ override suspend fun onDisplayRemovalAction(instance: TestPerDisplayInstance) {
+ removalActions += instance
+ }
+}
+
+data class TestPerDisplayInstance(val displayId: Int)
+
+val Kosmos.fakePerDisplayStore by
+ Kosmos.Fixture {
+ FakePerDisplayStore(
+ backgroundApplicationScope = applicationCoroutineScope,
+ displayRepository = displayRepository,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
index c0152b26d7a3..41402badf141 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
@@ -17,7 +17,6 @@
package com.android.systemui.flags
import android.platform.test.annotations.EnableFlags
-import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
@@ -36,7 +35,6 @@ import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN,
FLAG_PREDICTIVE_BACK_SYSUI,
FLAG_SCENE_CONTAINER,
- FLAG_DEVICE_ENTRY_UDFPS_REFACTOR,
)
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt
index 4ccee6f52fa2..2aa27444a058 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt
@@ -22,18 +22,16 @@ import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
val Kosmos.deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor by
Kosmos.Fixture {
DeviceEntrySideFpsOverlayInteractor(
- applicationScope = testScope.backgroundScope,
context = applicationContext,
deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
sceneInteractor = sceneInteractor,
primaryBouncerInteractor = primaryBouncerInteractor,
alternateBouncerInteractor = alternateBouncerInteractor,
- keyguardUpdateMonitor = keyguardUpdateMonitor
+ keyguardUpdateMonitor = keyguardUpdateMonitor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt
new file mode 100644
index 000000000000..e16756befb03
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.systemui.qs.tiles.impl.hearingdevices
+
+import com.android.systemui.accessibility.qs.QSAccessibilityModule
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.qsEventLogger
+
+val Kosmos.qsHearingDevicesTileConfig by
+ Kosmos.Fixture { QSAccessibilityModule.provideHearingDevicesTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt
index c0d65a076ca0..2316a2fdcd2b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel
+package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel
import android.content.packageManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.commandline.commandRegistry
import com.android.systemui.util.time.fakeSystemClock
-val Kosmos.demoRonChipViewModel: DemoRonChipViewModel by
+val Kosmos.demoNotifChipViewModel: DemoNotifChipViewModel by
Kosmos.Fixture {
- DemoRonChipViewModel(
+ DemoNotifChipViewModel(
commandRegistry = commandRegistry,
packageManager = packageManager,
systemClock = fakeSystemClock,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
new file mode 100644
index 000000000000..af24c371d62b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.systemui.statusbar.chips.notification.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+
+val Kosmos.notifChipsViewModel: NotifChipsViewModel by
+ Kosmos.Fixture { NotifChipsViewModel(activeNotificationsInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
index 5382c1c4b8d0..0300bf4636ea 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
@@ -20,7 +20,8 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.chips.call.ui.viewmodel.callChipViewModel
import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.castToOtherDeviceChipViewModel
-import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
+import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
+import com.android.systemui.statusbar.chips.notification.ui.viewmodel.notifChipsViewModel
import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.screenRecordChipViewModel
import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
import com.android.systemui.statusbar.chips.statusBarChipsLogger
@@ -33,7 +34,8 @@ val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by
shareToAppChipViewModel = shareToAppChipViewModel,
castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel,
callChipViewModel = callChipViewModel,
- demoRonChipViewModel = demoRonChipViewModel,
+ notifChipsViewModel = notifChipsViewModel,
+ demoNotifChipViewModel = demoNotifChipViewModel,
logger = statusBarChipsLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
index edd660490e4d..9fa3abfdc95c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
@@ -19,11 +19,18 @@ package com.android.systemui.statusbar.core
import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
+import org.mockito.kotlin.mock
-class FakeStatusBarInitializer(
- private val statusBarViewController: PhoneStatusBarViewController,
- private val statusBarTransitions: PhoneStatusBarTransitions,
-) : StatusBarInitializer {
+class FakeStatusBarInitializer : StatusBarInitializer {
+
+ val statusBarViewController = mock<PhoneStatusBarViewController>()
+ val statusBarTransitions = mock<PhoneStatusBarTransitions>()
+
+ var startedByCoreStartable: Boolean = false
+ private set
+
+ var initializedByCentralSurfaces: Boolean = false
+ private set
override var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? = null
set(value) {
@@ -31,5 +38,11 @@ class FakeStatusBarInitializer(
value?.onStatusBarViewUpdated(statusBarViewController, statusBarTransitions)
}
- override fun initializeStatusBar() {}
+ override fun initializeStatusBar() {
+ initializedByCentralSurfaces = true
+ }
+
+ override fun start() {
+ startedByCoreStartable = true
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
index 73ed228f5aaa..8c218be6c982 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
@@ -16,14 +16,11 @@
package com.android.systemui.statusbar.core
-import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
-import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
+import com.android.systemui.statusbar.window.StatusBarWindowController
-class FakeStatusBarInitializerFactory(
- private val statusBarViewController: PhoneStatusBarViewController,
- private val statusBarTransitions: PhoneStatusBarTransitions,
-) : StatusBarInitializer.Factory {
+class FakeStatusBarInitializerFactory() : StatusBarInitializer.Factory {
- override fun create(displayId: Int): StatusBarInitializer =
- FakeStatusBarInitializer(statusBarViewController, statusBarTransitions)
+ override fun create(
+ statusBarWindowController: StatusBarWindowController
+ ): StatusBarInitializer = FakeStatusBarInitializer()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt
new file mode 100644
index 000000000000..0c2cba981824
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.systemui.statusbar.core
+
+import android.view.Display
+
+class FakeStatusBarInitializerStore : StatusBarInitializerStore {
+
+ private val initializers = mutableMapOf<Int, FakeStatusBarInitializer>()
+
+ override val defaultDisplay: FakeStatusBarInitializer
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): FakeStatusBarInitializer {
+ return initializers.computeIfAbsent(displayId) { FakeStatusBarInitializer() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt
new file mode 100644
index 000000000000..9197dcdc3f68
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.systemui.statusbar.core
+
+import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository
+import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository
+import kotlinx.coroutines.CoroutineScope
+import org.mockito.kotlin.mock
+
+class FakeStatusBarOrchestratorFactory : StatusBarOrchestrator.Factory {
+
+ private val createdOrchestrators = mutableMapOf<Int, StatusBarOrchestrator>()
+
+ fun createdOrchestratorForDisplay(displayId: Int): StatusBarOrchestrator? =
+ createdOrchestrators[displayId]
+
+ override fun create(
+ displayId: Int,
+ displayScope: CoroutineScope,
+ statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository,
+ statusBarModeRepository: StatusBarModePerDisplayRepository,
+ statusBarInitializer: StatusBarInitializer,
+ statusBarWindowController: StatusBarWindowController,
+ ): StatusBarOrchestrator =
+ mock<StatusBarOrchestrator>().also { createdOrchestrators[displayId] = it }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
index 7ad715ba5a89..303529b7f7b0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
@@ -19,20 +19,13 @@ package com.android.systemui.statusbar.core
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.statusbar.phone.phoneStatusBarTransitions
-import com.android.systemui.statusbar.phone.phoneStatusBarViewController
+import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
-val Kosmos.fakeStatusBarInitializer by
- Kosmos.Fixture {
- FakeStatusBarInitializer(phoneStatusBarViewController, phoneStatusBarTransitions)
- }
+val Kosmos.fakeStatusBarInitializer by Kosmos.Fixture { FakeStatusBarInitializer() }
var Kosmos.statusBarInitializer by Kosmos.Fixture { fakeStatusBarInitializer }
-val Kosmos.fakeStatusBarInitializerFactory by
- Kosmos.Fixture {
- FakeStatusBarInitializerFactory(phoneStatusBarViewController, phoneStatusBarTransitions)
- }
+val Kosmos.fakeStatusBarInitializerFactory by Kosmos.Fixture { FakeStatusBarInitializerFactory() }
var Kosmos.statusBarInitializerFactory: StatusBarInitializer.Factory by
Kosmos.Fixture { fakeStatusBarInitializerFactory }
@@ -41,7 +34,13 @@ val Kosmos.multiDisplayStatusBarInitializerStore by
Kosmos.Fixture {
MultiDisplayStatusBarInitializerStore(
applicationCoroutineScope,
- fakeStatusBarInitializerFactory,
displayRepository,
+ fakeStatusBarInitializerFactory,
+ fakeStatusBarWindowControllerStore,
)
}
+
+val Kosmos.fakeStatusBarInitializerStore by Kosmos.Fixture { FakeStatusBarInitializerStore() }
+
+var Kosmos.statusBarInitializerStore: StatusBarInitializerStore by
+ Kosmos.Fixture { fakeStatusBarInitializerStore }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
index 54de293b8911..87f7142b8817 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
@@ -16,7 +16,11 @@
package com.android.systemui.statusbar.core
+import android.content.testableContext
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.display.data.repository.displayScopeRepository
+import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.mockDemoModeController
@@ -24,20 +28,25 @@ import com.android.systemui.plugins.mockPluginDependencyProvider
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.shade.mockNotificationShadeWindowViewController
import com.android.systemui.shade.mockShadeSurface
-import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
+import com.android.systemui.statusbar.data.repository.statusBarModeRepository
import com.android.systemui.statusbar.mockNotificationRemoteInputManager
import com.android.systemui.statusbar.phone.mockAutoHideController
+import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository
import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
-import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.fakeStatusBarWindowController
+import com.android.systemui.statusbar.window.statusBarWindowControllerStore
import com.android.wm.shell.bubbles.bubblesOptional
val Kosmos.statusBarOrchestrator by
Kosmos.Fixture {
StatusBarOrchestrator(
+ testableContext.displayId,
applicationCoroutineScope,
+ fakeStatusBarWindowStatePerDisplayRepository,
+ fakeStatusBarModePerDisplayRepository,
fakeStatusBarInitializer,
- fakeStatusBarModeRepository,
- fakeStatusBarWindowControllerStore,
+ fakeStatusBarWindowController,
mockDemoModeController,
mockPluginDependencyProvider,
mockAutoHideController,
@@ -45,8 +54,28 @@ val Kosmos.statusBarOrchestrator by
{ mockNotificationShadeWindowViewController },
mockShadeSurface,
bubblesOptional,
- statusBarWindowStateRepositoryStore,
+ dumpManager,
powerInteractor,
primaryBouncerInteractor,
)
}
+
+val Kosmos.fakeStatusBarOrchestratorFactory by Kosmos.Fixture { FakeStatusBarOrchestratorFactory() }
+
+var Kosmos.statusBarOrchestratorFactory: StatusBarOrchestrator.Factory by
+ Kosmos.Fixture { fakeStatusBarOrchestratorFactory }
+
+val Kosmos.multiDisplayStatusBarStarter by
+ Kosmos.Fixture {
+ MultiDisplayStatusBarStarter(
+ applicationCoroutineScope,
+ displayScopeRepository,
+ statusBarOrchestratorFactory,
+ statusBarWindowStateRepositoryStore,
+ statusBarModeRepository,
+ displayRepository,
+ statusBarInitializerStore,
+ statusBarWindowControllerStore,
+ statusBarInitializerStore,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
index 60690838fcdf..285cebb96cae 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
@@ -20,7 +20,6 @@ import android.view.Display
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.data.model.StatusBarAppearance
import com.android.systemui.statusbar.data.model.StatusBarMode
-import com.google.common.truth.Truth.assertThat
import dagger.Binds
import dagger.Module
import javax.inject.Inject
@@ -37,7 +36,6 @@ class FakeStatusBarModeRepository @Inject constructor() : StatusBarModeRepositor
FakeStatusBarModePerDisplayRepository()
override fun forDisplay(displayId: Int): FakeStatusBarModePerDisplayRepository {
- assertThat(displayId).isEqualTo(DISPLAY_ID)
return defaultDisplay
}
}
@@ -51,6 +49,7 @@ class FakeStatusBarModePerDisplayRepository : StatusBarModePerDisplayRepository
override fun showTransient() {
isTransientShown.value = true
}
+
override fun clearTransient() {
isTransientShown.value = false
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
index 0f2b477db67a..12db2f74197d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
@@ -18,6 +18,9 @@ package com.android.systemui.statusbar.data.repository
import com.android.systemui.kosmos.Kosmos
+val Kosmos.fakeStatusBarModePerDisplayRepository by
+ Kosmos.Fixture { FakeStatusBarModePerDisplayRepository() }
+
val Kosmos.statusBarModeRepository: StatusBarModeRepositoryStore by
Kosmos.Fixture { fakeStatusBarModeRepository }
val Kosmos.fakeStatusBarModeRepository by Kosmos.Fixture { FakeStatusBarModeRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index fc4f05df26ed..7f4c670b05aa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -69,6 +69,9 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
+import com.android.systemui.statusbar.notification.row.icon.AppIconProviderImpl
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProviderImpl
+import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger
import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -99,7 +102,7 @@ import org.mockito.Mockito
class ExpandableNotificationRowBuilder(
private val context: Context,
dependency: TestableDependency,
- private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
+ private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic(),
) {
private val mMockLogger: ExpandableNotificationRowLogger
@@ -161,21 +164,21 @@ class ExpandableNotificationRowBuilder(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP,
"true",
- true
+ true,
)
setProperty(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_ENABLED,
"true",
- true
+ true,
)
setProperty(
DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P,
"false",
- true
+ true,
)
- }
+ },
)
val remoteViewsFactories = getNotifRemoteViewsFactoryContainer(featureFlags)
val remoteInputManager = Mockito.mock(NotificationRemoteInputManager::class.java, STUB_ONLY)
@@ -192,21 +195,21 @@ class ExpandableNotificationRowBuilder(
Mockito.mock(KeyguardDismissUtil::class.java, STUB_ONLY),
remoteInputManager = remoteInputManager,
smartReplyController = mSmartReplyController,
- context = context
+ context = context,
),
smartActionsInflater =
SmartActionInflaterImpl(
constants = mSmartReplyConstants,
activityStarter = Mockito.mock(ActivityStarter::class.java, STUB_ONLY),
smartReplyController = mSmartReplyController,
- headsUpManager = mHeadsUpManager
- )
+ headsUpManager = mHeadsUpManager,
+ ),
)
val notifLayoutInflaterFactoryProvider =
object : NotifLayoutInflaterFactory.Provider {
override fun provide(
row: ExpandableNotificationRow,
- layoutType: Int
+ layoutType: Int,
): NotifLayoutInflaterFactory =
NotifLayoutInflaterFactory(row, layoutType, remoteViewsFactories)
}
@@ -270,14 +273,14 @@ class ExpandableNotificationRowBuilder(
whenever(
mOnUserInteractionCallback.registerFutureDismissal(
ArgumentMatchers.any(),
- ArgumentMatchers.anyInt()
+ ArgumentMatchers.anyInt(),
)
)
.thenReturn(mFutureDismissalRunnable)
}
private fun getNotifRemoteViewsFactoryContainer(
- featureFlags: FeatureFlags,
+ featureFlags: FeatureFlags
): NotifRemoteViewsFactoryContainer {
return NotifRemoteViewsFactoryContainerImpl(
featureFlags,
@@ -285,6 +288,10 @@ class ExpandableNotificationRowBuilder(
BigPictureLayoutInflaterFactory(),
NotificationOptimizedLinearLayoutFactory(),
{ Mockito.mock(NotificationViewFlipperFactory::class.java) },
+ NotificationRowIconViewInflaterFactory(
+ AppIconProviderImpl(context),
+ NotificationIconStyleProviderImpl(),
+ ),
)
}
@@ -293,7 +300,7 @@ class ExpandableNotificationRowBuilder(
NotificationChannel(
notification.channelId,
notification.channelId,
- NotificationManager.IMPORTANCE_DEFAULT
+ NotificationManager.IMPORTANCE_DEFAULT,
)
channel.isBlockable = true
val entry =
@@ -321,7 +328,7 @@ class ExpandableNotificationRowBuilder(
private fun generateRow(
entry: NotificationEntry,
- @InflationFlag extraInflationFlags: Int
+ @InflationFlag extraInflationFlags: Int,
): ExpandableNotificationRow {
// NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
// set, but we do not want to override an existing value that is needed by a specific test.
@@ -329,7 +336,7 @@ class ExpandableNotificationRowBuilder(
val rowInflaterTask =
RowInflaterTask(
mFakeSystemClock,
- Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY)
+ Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY),
)
val row = rowInflaterTask.inflateSynchronously(context, null, entry)
@@ -364,7 +371,7 @@ class ExpandableNotificationRowBuilder(
mSmartReplyController,
featureFlags,
Mockito.mock(IStatusBarService::class.java, STUB_ONLY),
- Mockito.mock(UiEventLogger::class.java, STUB_ONLY)
+ Mockito.mock(UiEventLogger::class.java, STUB_ONLY),
)
row.setAboveShelfChangedListener { aboveShelf: Boolean -> }
mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt
new file mode 100644
index 000000000000..08c6bbab6dd6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.systemui.statusbar.notification.row.icon
+
+import android.content.applicationContext
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.appIconProvider by Kosmos.Fixture { AppIconProviderImpl(applicationContext) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt
new file mode 100644
index 000000000000..611c90a6f4e8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.systemui.statusbar.notification.row.icon
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.notificationIconStyleProvider by Kosmos.Fixture { NotificationIconStyleProviderImpl() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt
new file mode 100644
index 000000000000..14f4d75d5647
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.systemui.statusbar.policy
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeBluetoothController by Kosmos.Fixture { FakeBluetoothController() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt
new file mode 100644
index 000000000000..4876cd8a6086
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.systemui.statusbar.policy
+
+import android.bluetooth.BluetoothAdapter
+import com.android.internal.annotations.VisibleForTesting
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.systemui.statusbar.policy.BluetoothController.Callback
+import java.io.PrintWriter
+import java.util.Collections
+import java.util.concurrent.Executor
+
+class FakeBluetoothController : BluetoothController {
+
+ private var callbacks = mutableListOf<Callback>()
+ private var enabled = false
+
+ override fun addCallback(listener: Callback) {
+ callbacks += listener
+ listener.onBluetoothStateChange(isBluetoothEnabled)
+ }
+
+ override fun removeCallback(listener: Callback) {
+ callbacks -= listener
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {}
+
+ override fun isBluetoothSupported(): Boolean = false
+
+ override fun isBluetoothEnabled(): Boolean = enabled
+
+ override fun getBluetoothState(): Int = 0
+
+ override fun isBluetoothConnected(): Boolean = false
+
+ override fun isBluetoothConnecting(): Boolean = false
+
+ override fun isBluetoothAudioProfileOnly(): Boolean = false
+
+ override fun isBluetoothAudioActive(): Boolean = false
+
+ override fun getConnectedDeviceName(): String? = null
+
+ override fun setBluetoothEnabled(enabled: Boolean) {
+ this.enabled = enabled
+ callbacks.forEach { it.onBluetoothStateChange(enabled) }
+ }
+
+ override fun canConfigBluetooth(): Boolean = false
+
+ override fun getConnectedDevices(): MutableList<CachedBluetoothDevice> = Collections.emptyList()
+
+ override fun addOnMetadataChangedListener(
+ device: CachedBluetoothDevice?,
+ executor: Executor?,
+ listener: BluetoothAdapter.OnMetadataChangedListener?,
+ ) {}
+
+ override fun removeOnMetadataChangedListener(
+ device: CachedBluetoothDevice?,
+ listener: BluetoothAdapter.OnMetadataChangedListener?,
+ ) {}
+
+ /** Trigger the [Callback.onBluetoothDevicesChanged] method for all registered callbacks. */
+ @VisibleForTesting
+ fun onBluetoothDevicesChanged() {
+ callbacks.forEach { it.onBluetoothDevicesChanged() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
index 2205a3b6e084..cbaf2bd65083 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
@@ -21,6 +21,9 @@ import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.settings.displayTracker
import com.android.systemui.statusbar.commandQueue
+val Kosmos.fakeStatusBarWindowStatePerDisplayRepository by
+ Kosmos.Fixture { FakeStatusBarWindowStatePerDisplayRepository() }
+
val Kosmos.fakeStatusBarWindowStateRepositoryStore by
Kosmos.Fixture { FakeStatusBarWindowStateRepositoryStore() }
diff --git a/packages/Vcn/OWNERS b/packages/Vcn/OWNERS
new file mode 100644
index 000000000000..ff2146e76b47
--- /dev/null
+++ b/packages/Vcn/OWNERS
@@ -0,0 +1,6 @@
+evitayan@google.com
+nharold@google.com
+benedictwong@google.com #{LAST_RESORT_SUGGESTION}
+yangji@google.com #{LAST_RESORT_SUGGESTION}
+
+include platform/packages/modules/common:/MODULES_OWNERS \ No newline at end of file
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index bfa801f30955..8e884bc3484b 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -372,6 +372,7 @@ java_library {
android_ravenwood_libgroup {
name: "ravenwood-runtime",
data: [
+ ":system-build.prop",
":framework-res",
":ravenwood-empty-res",
":framework-platform-compat-config",
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 5894476b9201..afa7a6c51abb 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -20,12 +20,14 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_R
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.getRavenwoodRuntimePath;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.ResourcesManager;
@@ -81,6 +83,8 @@ public class RavenwoodRuntimeEnvironmentController {
private static final String MAIN_THREAD_NAME = "RavenwoodMain";
private static final String RAVENWOOD_NATIVE_SYSPROP_NAME = "ravenwood_sysprop";
private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
+ private static final String RAVENWOOD_BUILD_PROP =
+ getRavenwoodRuntimePath() + "ravenwood-data/build.prop";
/**
* When enabled, attempt to dump all thread stacks just before we hit the
@@ -158,7 +162,8 @@ public class RavenwoodRuntimeEnvironmentController {
System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));
// Do the basic set up for the android sysprops.
- setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES);
+ RavenwoodSystemProperties.initialize(RAVENWOOD_BUILD_PROP);
+ setSystemProperties(null);
// Make sure libandroid_runtime is loaded.
RavenwoodNativeLoader.loadFrameworkNativeCode();
@@ -220,14 +225,9 @@ public class RavenwoodRuntimeEnvironmentController {
ActivityManager.init$ravenwood(config.mCurrentUser);
- final HandlerThread main;
- if (config.mProvideMainThread) {
- main = new HandlerThread(MAIN_THREAD_NAME);
- main.start();
- Looper.setMainLooperForTest(main.getLooper());
- } else {
- main = null;
- }
+ final var main = new HandlerThread(MAIN_THREAD_NAME);
+ main.start();
+ Looper.setMainLooperForTest(main.getLooper());
final boolean isSelfInstrumenting =
Objects.equals(config.mTestPackageName, config.mTargetPackageName);
@@ -319,17 +319,15 @@ public class RavenwoodRuntimeEnvironmentController {
}
sMockUiAutomation.dropShellPermissionIdentity();
- if (config.mProvideMainThread) {
- Looper.getMainLooper().quit();
- Looper.clearMainLooperForTest();
- }
+ Looper.getMainLooper().quit();
+ Looper.clearMainLooperForTest();
ActivityManager.reset$ravenwood();
LocalServices.removeAllServicesForTest();
ServiceManager.reset$ravenwood();
- setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES);
+ setSystemProperties(null);
if (sOriginalIdentityToken != -1) {
Binder.restoreCallingIdentity(sOriginalIdentityToken);
}
@@ -388,9 +386,10 @@ public class RavenwoodRuntimeEnvironmentController {
/**
* Set the current configuration to the actual SystemProperties.
*/
- private static void setSystemProperties(RavenwoodSystemProperties systemProperties) {
+ private static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) {
SystemProperties.clearChangeCallbacksForTest();
RavenwoodRuntimeNative.clearSystemProperties();
+ if (systemProperties == null) systemProperties = new RavenwoodSystemProperties();
sProps = new RavenwoodSystemProperties(systemProperties, true);
for (var entry : systemProperties.getValues().entrySet()) {
RavenwoodRuntimeNative.setSystemProperty(entry.getKey(), entry.getValue());
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
index 446f819ad41b..1f6e11dd5cf2 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
@@ -152,7 +152,10 @@ public final class RavenwoodConfig {
/**
* Configure a "main" thread to be available for the duration of the test, as defined
* by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments.
+ *
+ * @deprecated
*/
+ @Deprecated
public Builder setProvideMainThread(boolean provideMainThread) {
mConfig.mProvideMainThread = provideMainThread;
return this;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 4196d8e22610..93a6806ed1f4 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -139,7 +139,10 @@ public final class RavenwoodRule implements TestRule {
/**
* Configure a "main" thread to be available for the duration of the test, as defined
* by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments.
+ *
+ * @deprecated
*/
+ @Deprecated
public Builder setProvideMainThread(boolean provideMainThread) {
mBuilder.setProvideMainThread(provideMainThread);
return this;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index f1e1ef672871..ced151927fdc 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -18,12 +18,94 @@ package android.platform.test.ravenwood;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_SYSPROP;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class RavenwoodSystemProperties {
+ private static final String TAG = "RavenwoodSystemProperties";
+
+ private static final Map<String, String> sDefaultValues = new HashMap<>();
+
+ private static final String[] PARTITIONS = {
+ "bootimage",
+ "odm",
+ "product",
+ "system",
+ "system_ext",
+ "vendor",
+ "vendor_dlkm",
+ };
+
+ /**
+ * More info about property file loading: system/core/init/property_service.cpp
+ * In the following logic, the only partition we would need to consider is "system",
+ * since we only read from system-build.prop
+ */
+ static void initialize(String propFile) {
+ // Load all properties from build.prop
+ try {
+ Files.readAllLines(Path.of(propFile)).stream()
+ .map(String::trim)
+ .filter(s -> !s.startsWith("#"))
+ .map(s -> s.split("\\s*=\\s*", 2))
+ .filter(a -> a.length == 2)
+ .forEach(a -> sDefaultValues.put(a[0], a[1]));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ // If ro.product.${name} is not set, derive from ro.product.${partition}.${name}
+ // If ro.product.cpu.abilist* is not set, derive from ro.${partition}.product.cpu.abilist*
+ for (var entry : Set.copyOf(sDefaultValues.entrySet())) {
+ final String key;
+ if (entry.getKey().startsWith("ro.product.system.")) {
+ var name = entry.getKey().substring(18);
+ key = "ro.product." + name;
+
+ } else if (entry.getKey().startsWith("ro.system.product.cpu.abilist")) {
+ var name = entry.getKey().substring(22);
+ key = "ro.product.cpu." + name;
+ } else {
+ continue;
+ }
+ if (!sDefaultValues.containsKey(key)) {
+ sDefaultValues.put(key, entry.getValue());
+ }
+ }
+
+ // Some other custom values
+ sDefaultValues.put("ro.board.first_api_level", "1");
+ sDefaultValues.put("ro.product.first_api_level", "1");
+ sDefaultValues.put("ro.soc.manufacturer", "Android");
+ sDefaultValues.put("ro.soc.model", "Ravenwood");
+ sDefaultValues.put(RAVENWOOD_SYSPROP, "1");
+
+ // Log all values
+ sDefaultValues.forEach((key, value) -> RavenwoodCommonUtils.log(TAG, key + "=" + value));
+
+ // Copy ro.product.* and ro.build.* to all partitions, just in case
+ // We don't want to log these because these are just a lot of duplicate values
+ for (var entry : Set.copyOf(sDefaultValues.entrySet())) {
+ var key = entry.getKey();
+ if (key.startsWith("ro.product.") || key.startsWith("ro.build.")) {
+ var name = key.substring(3);
+ for (String partition : PARTITIONS) {
+ var newKey = "ro." + partition + "." + name;
+ if (!sDefaultValues.containsKey(newKey)) {
+ sDefaultValues.put(newKey, entry.getValue());
+ }
+ }
+ }
+ }
+ }
+
private volatile boolean mIsImmutable;
private final Map<String, String> mValues = new HashMap<>();
@@ -35,47 +117,15 @@ public class RavenwoodSystemProperties {
private final Set<String> mKeyWritable = new HashSet<>();
public RavenwoodSystemProperties() {
- // TODO: load these values from build.prop generated files
- setValueForPartitions("product.brand", "Android");
- setValueForPartitions("product.device", "Ravenwood");
- setValueForPartitions("product.manufacturer", "Android");
- setValueForPartitions("product.model", "Ravenwood");
- setValueForPartitions("product.name", "Ravenwood");
-
- setValueForPartitions("product.cpu.abilist", "x86_64");
- setValueForPartitions("product.cpu.abilist32", "");
- setValueForPartitions("product.cpu.abilist64", "x86_64");
-
- setValueForPartitions("build.date", "Thu Jan 01 00:00:00 GMT 2024");
- setValueForPartitions("build.date.utc", "1704092400");
- setValueForPartitions("build.id", "MAIN");
- setValueForPartitions("build.tags", "dev-keys");
- setValueForPartitions("build.type", "userdebug");
- setValueForPartitions("build.version.all_codenames", "REL");
- setValueForPartitions("build.version.codename", "REL");
- setValueForPartitions("build.version.incremental", "userdebug.ravenwood.20240101");
- setValueForPartitions("build.version.known_codenames", "REL");
- setValueForPartitions("build.version.release", "14");
- setValueForPartitions("build.version.release_or_codename", "VanillaIceCream");
- setValueForPartitions("build.version.sdk", "34");
-
- setValue("ro.board.first_api_level", "1");
- setValue("ro.product.first_api_level", "1");
-
- setValue("ro.soc.manufacturer", "Android");
- setValue("ro.soc.model", "Ravenwood");
-
- setValue("ro.debuggable", "1");
-
- setValue(RAVENWOOD_SYSPROP, "1");
+ mValues.putAll(sDefaultValues);
}
/** Copy constructor */
public RavenwoodSystemProperties(RavenwoodSystemProperties source, boolean immutable) {
- this.mKeyReadable.addAll(source.mKeyReadable);
- this.mKeyWritable.addAll(source.mKeyWritable);
- this.mValues.putAll(source.mValues);
- this.mIsImmutable = immutable;
+ mKeyReadable.addAll(source.mKeyReadable);
+ mKeyWritable.addAll(source.mKeyWritable);
+ mValues.putAll(source.mValues);
+ mIsImmutable = immutable;
}
public Map<String, String> getValues() {
@@ -123,36 +173,12 @@ public class RavenwoodSystemProperties {
return mKeyWritable.contains(key);
}
- private static final String[] PARTITIONS = {
- "bootimage",
- "odm",
- "product",
- "system",
- "system_ext",
- "vendor",
- "vendor_dlkm",
- };
-
private void ensureNotImmutable() {
if (mIsImmutable) {
throw new RuntimeException("Unable to update immutable instance");
}
}
- /**
- * Set the given property for all possible partitions where it could be defined. For
- * example, the value of {@code ro.build.type} is typically also mirrored under
- * {@code ro.system.build.type}, etc.
- */
- private void setValueForPartitions(String key, String value) {
- ensureNotImmutable();
-
- setValue("ro." + key, value);
- for (String partition : PARTITIONS) {
- setValue("ro." + partition + "." + key, value);
- }
- }
-
public void setValue(String key, Object value) {
ensureNotImmutable();
@@ -195,11 +221,4 @@ public class RavenwoodSystemProperties {
return key;
}
}
-
- /**
- * Return an immutable, default instance.
- */
- // Create a default instance, and make an immutable copy of it.
- public static final RavenwoodSystemProperties DEFAULT_VALUES =
- new RavenwoodSystemProperties(new RavenwoodSystemProperties(), true);
}
diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp
index ac499b966afe..d7f4b3e2955d 100644
--- a/ravenwood/tests/bivalenttest/Android.bp
+++ b/ravenwood/tests/bivalenttest/Android.bp
@@ -54,34 +54,36 @@ android_ravenwood_test {
auto_gen_config: true,
}
-// TODO(b/371215487): migrate bivalenttest.ravenizer tests to another architecture
+android_test {
+ name: "RavenwoodBivalentTest_device",
-// android_test {
-// name: "RavenwoodBivalentTest_device",
-//
-// srcs: [
-// "test/**/*.java",
-// ],
-// static_libs: [
-// "junit",
-// "truth",
-//
-// "androidx.annotation_annotation",
-// "androidx.test.ext.junit",
-// "androidx.test.rules",
-//
-// "junit-params",
-// "platform-parametric-runner-lib",
-//
-// "ravenwood-junit",
-// ],
-// jni_libs: [
-// "libravenwoodbivalenttest_jni",
-// ],
-// test_suites: [
-// "device-tests",
-// ],
-// optimize: {
-// enabled: false,
-// },
-// }
+ srcs: [
+ "test/**/*.java",
+ ],
+ // TODO(b/371215487): migrate bivalenttest.ravenizer tests to another architecture
+ exclude_srcs: [
+ "test/**/ravenizer/*.java",
+ ],
+ static_libs: [
+ "junit",
+ "truth",
+
+ "androidx.annotation_annotation",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+
+ "junit-params",
+ "platform-parametric-runner-lib",
+
+ "ravenwood-junit",
+ ],
+ jni_libs: [
+ "libravenwoodbivalenttest_jni",
+ ],
+ test_suites: [
+ "device-tests",
+ ],
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/ravenwood/minimum-test/Android.bp b/ravenwood/tests/minimum-test/Android.bp
index e4ed3d5b0b73..e4ed3d5b0b73 100644
--- a/ravenwood/minimum-test/Android.bp
+++ b/ravenwood/tests/minimum-test/Android.bp
diff --git a/ravenwood/minimum-test/README.md b/ravenwood/tests/minimum-test/README.md
index 6b0abe968053..6b0abe968053 100644
--- a/ravenwood/minimum-test/README.md
+++ b/ravenwood/tests/minimum-test/README.md
diff --git a/ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java b/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
index 30abaa2e7d38..30abaa2e7d38 100644
--- a/ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
+++ b/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
diff --git a/ravenwood/mockito/Android.bp b/ravenwood/tests/mockito/Android.bp
index d91537bbc334..d91537bbc334 100644
--- a/ravenwood/mockito/Android.bp
+++ b/ravenwood/tests/mockito/Android.bp
diff --git a/ravenwood/mockito/AndroidManifest.xml b/ravenwood/tests/mockito/AndroidManifest.xml
index 15f0a2934b5f..15f0a2934b5f 100644
--- a/ravenwood/mockito/AndroidManifest.xml
+++ b/ravenwood/tests/mockito/AndroidManifest.xml
diff --git a/ravenwood/mockito/AndroidTest.xml b/ravenwood/tests/mockito/AndroidTest.xml
index 5ba9b1ff2cd8..5ba9b1ff2cd8 100644
--- a/ravenwood/mockito/AndroidTest.xml
+++ b/ravenwood/tests/mockito/AndroidTest.xml
diff --git a/ravenwood/mockito/README.md b/ravenwood/tests/mockito/README.md
index 4ceb795fe14a..4ceb795fe14a 100644
--- a/ravenwood/mockito/README.md
+++ b/ravenwood/tests/mockito/README.md
diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java
index d566977bd15c..d566977bd15c 100644
--- a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java
+++ b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java
diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java
index aa2b7611da37..aa2b7611da37 100644
--- a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java
+++ b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java
diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java
index fcc6c9cc447d..fcc6c9cc447d 100644
--- a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java
+++ b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java
diff --git a/ravenwood/resapk_test/Android.bp b/ravenwood/tests/resapk_test/Android.bp
index c14576550f78..c14576550f78 100644
--- a/ravenwood/resapk_test/Android.bp
+++ b/ravenwood/tests/resapk_test/Android.bp
diff --git a/ravenwood/resapk_test/apk/Android.bp b/ravenwood/tests/resapk_test/apk/Android.bp
index 10ed5e2f8410..10ed5e2f8410 100644
--- a/ravenwood/resapk_test/apk/Android.bp
+++ b/ravenwood/tests/resapk_test/apk/Android.bp
diff --git a/ravenwood/resapk_test/apk/AndroidManifest.xml b/ravenwood/tests/resapk_test/apk/AndroidManifest.xml
index f34d8b2f4e81..f34d8b2f4e81 100644
--- a/ravenwood/resapk_test/apk/AndroidManifest.xml
+++ b/ravenwood/tests/resapk_test/apk/AndroidManifest.xml
diff --git a/ravenwood/resapk_test/apk/res/values/strings.xml b/ravenwood/tests/resapk_test/apk/res/values/strings.xml
index 23d4c0f21007..23d4c0f21007 100644
--- a/ravenwood/resapk_test/apk/res/values/strings.xml
+++ b/ravenwood/tests/resapk_test/apk/res/values/strings.xml
diff --git a/ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
index e547114bbe40..e547114bbe40 100644
--- a/ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
+++ b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
diff --git a/ravenwood/runtime-test/Android.bp b/ravenwood/tests/runtime-test/Android.bp
index 410292001670..410292001670 100644
--- a/ravenwood/runtime-test/Android.bp
+++ b/ravenwood/tests/runtime-test/Android.bp
diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java
index 633ed4e9d10a..633ed4e9d10a 100644
--- a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java
+++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java
diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
index c2230c739ccf..c2230c739ccf 100644
--- a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
+++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
diff --git a/ravenwood/services-test/Android.bp b/ravenwood/tests/services-test/Android.bp
index 39858f05e80d..39858f05e80d 100644
--- a/ravenwood/services-test/Android.bp
+++ b/ravenwood/tests/services-test/Android.bp
diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java
index f833782bc8bb..f833782bc8bb 100644
--- a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java
+++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java
diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
index b3d3963270ee..b3d3963270ee 100644
--- a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
+++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
diff --git a/ravenwood/tools/hoststubgen/.gitignore b/ravenwood/tools/hoststubgen/.gitignore
new file mode 100644
index 000000000000..82158c9b25e3
--- /dev/null
+++ b/ravenwood/tools/hoststubgen/.gitignore
@@ -0,0 +1,4 @@
+framework-all-stub-out
+out/
+*-out/
+*.log
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/ravenwood/tools/hoststubgen/Android.bp
index a5ff4964b0a4..a5ff4964b0a4 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/ravenwood/tools/hoststubgen/Android.bp
diff --git a/tools/hoststubgen/README.md b/ravenwood/tools/hoststubgen/README.md
index 1a895dc7dfce..615e7671bea1 100644
--- a/tools/hoststubgen/README.md
+++ b/ravenwood/tools/hoststubgen/README.md
@@ -11,7 +11,7 @@ used at runtime.
- HostStubGen itself is design to be agnostic to Android. It doesn't use any Android APIs
(hidden or not). But it may use Android specific knowledge -- e.g. as of now,
-AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classes.
+AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classes.
- `test-tiny-framework/` contains basic tests that are agnostic to Android.
@@ -20,19 +20,16 @@ AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classe
## Directories and files
-- `hoststubgen/`
- Contains source code of the "hoststubgen" tool and relevant code
+- `src/`
- - `src/`
+ HostStubGen tool source code.
- HostStubGen tool source code.
+- `annotations-src/` See `Android.bp`.
+- `helper-framework-buildtime-src/` See `Android.bp`.
+- `helper-framework-runtime-src/` See `Android.bp`.
+- `helper-runtime-src/` See `Android.bp`.
- - `annotations-src/` See `Android.bp`.
- - `helper-framework-buildtime-src/` See `Android.bp`.
- - `helper-framework-runtime-src/` See `Android.bp`.
- - `helper-runtime-src/` See `Android.bp`.
-
- - `test-tiny-framework/` See `README.md` in it.
+- `test-tiny-framework/` See `README.md` in it.
- `scripts`
- `dump-jar.sh`
@@ -78,4 +75,4 @@ $ ./scripts/run-all-tests.sh
- At some point, we can move or delete all Android specific code to `frameworks/base/ravenwood`.
- `helper-framework-*-src` should be moved to `frameworks/base/ravenwood`
- - `test-framework` should be deleted. \ No newline at end of file
+ - `test-framework` should be deleted.
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java
index a774336a897c..a774336a897c 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java
+++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestClassLoadHook.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java
index 501fd652145e..501fd652145e 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java
+++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestIgnore.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java
index 06ad1c266a14..06ad1c266a14 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java
+++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestKeep.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java
index bc9471b84b97..bc9471b84b97 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java
+++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirect.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java
index 28ad236a66f3..28ad236a66f3 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java
+++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRedirectionClass.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java
index 46e5078fb05d..46e5078fb05d 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java
+++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestRemove.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
index eec72269e0d3..eec72269e0d3 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
+++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStaticInitializerKeep.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java
index 510a67e0aaed..510a67e0aaed 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java
+++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestSubstitute.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java
index cd1bef4be505..cd1bef4be505 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java
+++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestThrow.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java
index 3d1ddea2cbb7..3d1ddea2cbb7 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java
+++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassKeep.java
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java
index b10f0ff1a4b1..b10f0ff1a4b1 100644
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java
+++ b/ravenwood/tools/hoststubgen/annotations-src/android/hosttest/annotation/tests/HostSideTestSuppress.java
diff --git a/tools/hoststubgen/common.sh b/ravenwood/tools/hoststubgen/common.sh
index b49ee39a3142..b49ee39a3142 100644
--- a/tools/hoststubgen/common.sh
+++ b/ravenwood/tools/hoststubgen/common.sh
diff --git a/tools/hoststubgen/hoststubgen/framework-policy-override.txt b/ravenwood/tools/hoststubgen/framework-policy-override.txt
index af3789e270a4..af3789e270a4 100644
--- a/tools/hoststubgen/hoststubgen/framework-policy-override.txt
+++ b/ravenwood/tools/hoststubgen/framework-policy-override.txt
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
index b01710347537..b01710347537 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
+++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
index 18ef1bab203e..18ef1bab203e 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
+++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
index 99e38c0b1725..99e38c0b1725 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
+++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
index 4933cf8784d9..4933cf8784d9 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
+++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java
index c54c2c111229..c54c2c111229 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java
+++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestException.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java
index 29f7be008eef..29f7be008eef 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java
+++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestSuite.java
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
index 78fd8f7f960a..78fd8f7f960a 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
+++ b/ravenwood/tools/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt
index 001943c18d6b..001943c18d6b 100644
--- a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
+++ b/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt
diff --git a/tools/hoststubgen/hoststubgen/invoketest/Android.bp b/ravenwood/tools/hoststubgen/invoketest/Android.bp
index 7e90e421a1f9..7e90e421a1f9 100644
--- a/tools/hoststubgen/hoststubgen/invoketest/Android.bp
+++ b/ravenwood/tools/hoststubgen/invoketest/Android.bp
diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/ravenwood/tools/hoststubgen/invoketest/hoststubgen-invoke-test.sh
index 084448d0a797..084448d0a797 100755
--- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
+++ b/ravenwood/tools/hoststubgen/invoketest/hoststubgen-invoke-test.sh
diff --git a/tools/hoststubgen/hoststubgen/jarjar-rules.txt b/ravenwood/tools/hoststubgen/jarjar-rules.txt
index b1f2fc21c8e9..b1f2fc21c8e9 100644
--- a/tools/hoststubgen/hoststubgen/jarjar-rules.txt
+++ b/ravenwood/tools/hoststubgen/jarjar-rules.txt
diff --git a/tools/hoststubgen/scripts/Android.bp b/ravenwood/tools/hoststubgen/scripts/Android.bp
index b1ba07ec540d..b1ba07ec540d 100644
--- a/tools/hoststubgen/scripts/Android.bp
+++ b/ravenwood/tools/hoststubgen/scripts/Android.bp
diff --git a/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh b/ravenwood/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh
index c3605a9ffaa5..c3605a9ffaa5 100755
--- a/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh
+++ b/ravenwood/tools/hoststubgen/scripts/build-framework-hostside-jars-without-genrules.sh
diff --git a/tools/hoststubgen/scripts/dump-jar b/ravenwood/tools/hoststubgen/scripts/dump-jar
index 87652451359d..87652451359d 100755
--- a/tools/hoststubgen/scripts/dump-jar
+++ b/ravenwood/tools/hoststubgen/scripts/dump-jar
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
index f59e143c1e4e..f59e143c1e4e 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 6d8d7b768b91..6d8d7b768b91 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
index a218c5599553..a218c5599553 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
index 4bcee409aaec..4bcee409aaec 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
index 85064661cd2b..85064661cd2b 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 55e853e3e2fb..55e853e3e2fb 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
index 9045db210495..9045db210495 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenStats.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt
index 10179eefcb95..10179eefcb95 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Utils.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/Utils.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index a02082d12934..a02082d12934 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
index e2647eb13ed3..e2647eb13ed3 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
index 5e4e70f0cbaa..5e4e70f0cbaa 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
index 6b360b79c327..6b360b79c327 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
index 36adf0626415..36adf0626415 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
index f8bb526d0a86..f8bb526d0a86 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
index be3c59c80152..be3c59c80152 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
index d771003a955d..d771003a955d 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DefaultHookInjectingFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
index b8b0d8a31268..b8b0d8a31268 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
index 2f2f81b05ad1..2f2f81b05ad1 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
index b10165b835f2..b10165b835f2 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
index c5a2f9ff5e96..c5a2f9ff5e96 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 474da6dfa1b9..474da6dfa1b9 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
index 59fa464a7212..59fa464a7212 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt
index 00e7d77fa6e7..00e7d77fa6e7 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/KeepNativeFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
index f99ce906240a..f99ce906240a 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt
index c67e6714d4c2..c67e6714d4c2 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/PackageFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt
index 18a1e16bcf3a..18a1e16bcf3a 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SanitizationFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
index fd7474b55fa6..fd7474b55fa6 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index caf80ebec0c9..caf80ebec0c9 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
index d45f41407a52..d45f41407a52 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
index a78c6552b8d0..a78c6552b8d0 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
index d6aa7617fd59..d6aa7617fd59 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt
index 1b3d79cddb8e..1b3d79cddb8e 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/utils/Trie.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
index 261ef59c45c7..261ef59c45c7 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
index 55d0c0e555f1..55d0c0e555f1 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt
index dc4f26bdda34..dc4f26bdda34 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/Helper.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index 567a69e43b58..567a69e43b58 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
index e90ecd7ef678..e90ecd7ef678 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/PackageRedirectRemapper.kt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp
index ba2c869adfe8..1570549ec27d 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/Android.bp
@@ -16,7 +16,7 @@ java_library {
static_libs: [
"hoststubgen-annotations",
],
- visibility: ["//frameworks/base/tools/hoststubgen:__subpackages__"],
+ visibility: ["//frameworks/base/ravenwood/tools/hoststubgen:__subpackages__"],
}
// Create stub/impl jars from "hoststubgen-test-tiny-framework", using the following 3 rules.
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml b/ravenwood/tools/hoststubgen/test-tiny-framework/AndroidTest-host.xml
index 84aad69c33bc..84aad69c33bc 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/AndroidTest-host.xml
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/AndroidTest-host.xml
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md b/ravenwood/tools/hoststubgen/test-tiny-framework/README.md
index 344b4e953b23..344b4e953b23 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/README.md
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
index de4cb0c536c1..de4cb0c536c1 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
index 3726ca972564..3726ca972564 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 103e152c7e39..103e152c7e39 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
index eeec554e954c..eeec554e954c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
index 0f8af92dc486..0f8af92dc486 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 3c138d21b75d..3c138d21b75d 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/run-test-manually.sh
index 80ebf3adab3d..80ebf3adab3d 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/run-test-manually.sh
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
index cee29dcd1d59..cee29dcd1d59 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
index 0a07c2b91fc3..0a07c2b91fc3 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/IPretendingAidl.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
index b1bedf4b6853..b1bedf4b6853 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
index 3415deb957ed..3415deb957ed 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
index f734790c8dd9..f734790c8dd9 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java
index e83163edb5e5..e83163edb5e5 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
index 3df21d9a5647..3df21d9a5647 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
index cc665de9cd01..cc665de9cd01 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
index f833ad814513..f833ad814513 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
index c023169b5601..c023169b5601 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
index f7cae7d255fe..f7cae7d255fe 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
index ec1efba99c77..ec1efba99c77 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
index 1ca653ec7da6..1ca653ec7da6 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
index 57c69a336654..57c69a336654 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
index 04a551c8c46e..04a551c8c46e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
index c7a29a1cc0f9..c7a29a1cc0f9 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
index c1ea2ee59fbb..c1ea2ee59fbb 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
index 941fcff31d8e..941fcff31d8e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
index 707bc0ebb4db..707bc0ebb4db 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
index 8319ced6109a..8319ced6109a 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
index 6a52e4401b45..6a52e4401b45 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/A.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
index 1374a288f7aa..1374a288f7aa 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/B.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
index 361a7fd04842..361a7fd04842 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/A.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
index 716595a44243..716595a44243 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/packagetest/sub/B.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
index 03c9e2a7b5bb..03c9e2a7b5bb 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
index 3ca8f1f172bd..3ca8f1f172bd 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
index a6c14f09b680..a6c14f09b680 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
index 2e353709fd1e..2e353709fd1e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
index fe4cee64b2f9..fe4cee64b2f9 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
index 12012fcdf6ca..12012fcdf6ca 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
index 8d48ee6f6858..8d48ee6f6858 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
index 6748430a8e6f..6748430a8e6f 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
index 58aa5c3b74eb..58aa5c3b74eb 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
index c1c3d624b126..c1c3d624b126 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
index 398b56975f1c..398b56975f1c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
index 44cbd8f9bffa..44cbd8f9bffa 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
index 42355a34b65c..42355a34b65c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
index 09c80992e450..09c80992e450 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
index 0806a478d756..0806a478d756 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
index eaa8528a3f31..eaa8528a3f31 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
index 778c5aaf27f0..778c5aaf27f0 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
index 493f7c83c0f0..493f7c83c0f0 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
index 2aa1de18b7f4..2aa1de18b7f4 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
index d9eae0934c42..d9eae0934c42 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
index 9ee42836ac9a..9ee42836ac9a 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
index 50ec2cbc9c6e..50ec2cbc9c6e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
index 3f3659644a80..3f3659644a80 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
index 960060c8a036..960060c8a036 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
index c678eaa789b0..c678eaa789b0 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
index 1cff484c3cd8..1cff484c3cd8 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
index 84e7173c71b8..84e7173c71b8 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java
index fa5866451e83..fa5866451e83 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/supported/UnsupportedClass.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
index 92f41ac63cdb..92f41ac63cdb 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java
index 1ae049371229..1ae049371229 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 68673dc2a5b8..68673dc2a5b8 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
index 1816b383f6f7..1816b383f6f7 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
index 5b2795c4cff2..5b2795c4cff2 100644
--- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
+++ b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
index d4e75d43a54a..d4e75d43a54a 100644
--- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
+++ b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt
index 081d03909926..081d03909926 100644
--- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt
+++ b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/utils/TrieTest.kt
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt
index 75e2536a98fa..75e2536a98fa 100644
--- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt
+++ b/ravenwood/tools/hoststubgen/test/com/android/hoststubgen/visitors/HelperTest.kt
diff --git a/services/Android.bp b/services/Android.bp
index 653cd3c3b680..f04c692c12d0 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -195,7 +195,17 @@ soong_config_module_type {
module_type: "java_library",
config_namespace: "system_services",
bool_variables: ["without_vibrator"],
- properties: ["vintf_fragments"],
+ properties: ["vintf_fragment_modules"],
+}
+
+vintf_fragment {
+ name: "manifest_services.xml",
+ src: "manifest_services.xml",
+}
+
+vintf_fragment {
+ name: "manifest_services_android.frameworks.vibrator.xml",
+ src: "manifest_services_android.frameworks.vibrator.xml",
}
system_java_library {
@@ -264,11 +274,11 @@ system_java_library {
soong_config_variables: {
without_vibrator: {
- vintf_fragments: [
+ vintf_fragment_modules: [
"manifest_services.xml",
],
conditions_default: {
- vintf_fragments: [
+ vintf_fragment_modules: [
"manifest_services.xml",
"manifest_services_android.frameworks.vibrator.xml",
],
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 034127c0420e..7057cc361a1a 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -45,6 +45,16 @@ flag {
}
flag {
+ name: "clear_shortcuts_when_activity_updates_to_service"
+ namespace: "accessibility"
+ description: "When an a11y activity is updated to an a11y service, clears the associated shortcuts so that we don't skip the AccessibilityServiceWarning."
+ bug: "358092445"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "compute_window_changes_on_a11y_v2"
namespace: "accessibility"
description: "Computes accessibility window changes in accessibility instead of wm package."
@@ -114,10 +124,13 @@ flag {
}
flag {
- name: "enable_magnification_follows_mouse"
+ name: "enable_magnification_follows_mouse_bugfix"
namespace: "accessibility"
description: "Whether to enable mouse following for fullscreen magnification"
bug: "354696546"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 1451dfaa7964..ec8908bc7c91 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2513,6 +2513,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private boolean readInstalledAccessibilityShortcutLocked(AccessibilityUserState userState,
List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos) {
if (!parsedAccessibilityShortcutInfos.equals(userState.mInstalledShortcuts)) {
+ if (Flags.clearShortcutsWhenActivityUpdatesToService()) {
+ List<String> componentNames = userState.mInstalledShortcuts.stream()
+ .filter(a11yActivity ->
+ !parsedAccessibilityShortcutInfos.contains(a11yActivity))
+ .map(a11yActivity -> a11yActivity.getComponentName().flattenToString())
+ .toList();
+ if (!componentNames.isEmpty()) {
+ enableShortcutsForTargets(
+ /* enable= */ false, UserShortcutType.ALL,
+ componentNames, userState.mUserId);
+ }
+ }
+
userState.mInstalledShortcuts.clear();
userState.mInstalledShortcuts.addAll(parsedAccessibilityShortcutInfos);
userState.updateTileServiceMapForAccessibilityActivityLocked();
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index a19fdddea49c..963334b07ea6 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -345,7 +345,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
@Override
void handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (Flags.enableMagnificationFollowsMouse()) {
+ if (Flags.enableMagnificationFollowsMouseBugfix()) {
if (mFullScreenMagnificationController.isActivated(mDisplayId)) {
// TODO(b/354696546): Allow mouse/stylus to activate whichever display they are
// over, rather than only interacting with the current display.
@@ -1206,7 +1206,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
protected void cacheDelayedMotionEvent(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
- if (Flags.enableMagnificationFollowsMouse()
+ if (Flags.enableMagnificationFollowsMouseBugfix()
&& !event.isFromSource(SOURCE_TOUCHSCREEN)) {
// Only touch events need to be cached and sent later.
return;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index 446123f07f64..fa86ba39bb1a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -146,7 +146,8 @@ public abstract class MagnificationGestureHandler extends BaseEventStreamTransfo
} break;
case SOURCE_MOUSE:
case SOURCE_STYLUS: {
- if (magnificationShortcutExists() && Flags.enableMagnificationFollowsMouse()) {
+ if (magnificationShortcutExists()
+ && Flags.enableMagnificationFollowsMouseBugfix()) {
handleMouseOrStylusEvent(event, rawEvent, policyFlags);
}
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 5d574083b326..89f14b09d397 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -68,7 +68,10 @@ import com.android.server.SystemService.TargetUser;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.Map;
import java.util.Objects;
+import java.util.WeakHashMap;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
@@ -81,7 +84,8 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
private final ServiceHelper mInternalServiceHelper;
private final ServiceConfig mServiceConfig;
private final Context mContext;
- private final Object mLock = new Object();
+ private final Map<String, Object> mLocks = new WeakHashMap<>();
+
public AppFunctionManagerServiceImpl(@NonNull Context context) {
this(
@@ -225,12 +229,6 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
.thenAccept(
canExecute -> {
if (!canExecute) {
- safeExecuteAppFunctionCallback.onResult(
- ExecuteAppFunctionResponse.newFailure(
- ExecuteAppFunctionResponse.RESULT_DENIED,
- "Caller does not have permission to execute the"
- + " appfunction",
- /* extras= */ null));
throw new SecurityException(
"Caller does not have permission to execute the"
+ " appfunction");
@@ -322,9 +320,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
THREAD_POOL_EXECUTOR.execute(
() -> {
try {
- // TODO(357551503): Instead of holding a global lock, hold a per-package
- // lock.
- synchronized (mLock) {
+ synchronized (getLockForPackage(callingPackage)) {
setAppFunctionEnabledInternalLocked(
callingPackage, functionIdentifier, userHandle, enabledState);
}
@@ -352,7 +348,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
* process.
*/
@WorkerThread
- @GuardedBy("mLock")
+ @GuardedBy("getLockForPackage(callingPackage)")
private void setAppFunctionEnabledInternalLocked(
@NonNull String callingPackage,
@NonNull String functionIdentifier,
@@ -547,6 +543,26 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
});
}
}
+ /**
+ * Retrieves the lock object associated with the given package name.
+ *
+ * This method returns the lock object from the {@code mLocks} map if it exists.
+ * If no lock is found for the given package name, a new lock object is created,
+ * stored in the map, and returned.
+ */
+ @VisibleForTesting
+ @NonNull
+ Object getLockForPackage(String callingPackage) {
+ // Synchronized the access to mLocks to prevent race condition.
+ synchronized (mLocks) {
+ // By using a WeakHashMap, we allow the garbage collector to reclaim memory by removing
+ // entries associated with unused callingPackage keys. Therefore, we remove the null
+ // values before getting/computing a new value. The goal is to not let the size of this
+ // map grow without an upper bound.
+ mLocks.values().removeAll(Collections.singleton(null)); // Remove null values
+ return mLocks.computeIfAbsent(callingPackage, k -> new Object());
+ }
+ }
private static class AppFunctionMetadataObserver implements ObserverCallback {
@Nullable private final MetadataSyncAdapter mPerUserMetadataSyncAdapter;
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index ba8448548365..1803424ae7d7 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -49,3 +49,10 @@ flag {
description: "Include the session id into the FillEventHistory events as part of ClientState"
bug: "333927465"
}
+
+flag {
+ name: "highlight_autofill_single_field"
+ namespace: "autofill"
+ description: "Highlight single field after autofill selection"
+ bug: "41496744"
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 2fa0e0d0d946..8f12b1db8f29 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -39,6 +39,7 @@ import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG;
import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
+import static android.service.autofill.Flags.highlightAutofillSingleField;
import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED;
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
@@ -49,7 +50,6 @@ import static android.view.autofill.AutofillManager.COMMIT_REASON_UNKNOWN;
import static android.view.autofill.AutofillManager.EXTRA_AUTOFILL_REQUEST_ID;
import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
-
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_EXPLICITLY_REQUESTED;
import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_NORMAL_TRIGGER;
@@ -104,7 +104,6 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUC
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.Activity;
import android.app.ActivityTaskManager;
import android.app.IAssistDataReceiver;
import android.app.PendingIntent;
@@ -143,7 +142,6 @@ import android.os.TransactionTooLargeException;
import android.service.assist.classification.FieldClassificationRequest;
import android.service.assist.classification.FieldClassificationResponse;
import android.service.autofill.AutofillFieldClassificationService.Scores;
-import android.service.autofill.AutofillService;
import android.service.autofill.CompositeUserData;
import android.service.autofill.ConvertCredentialResponse;
import android.service.autofill.Dataset;
@@ -186,7 +184,6 @@ import android.view.autofill.IAutoFillManagerClient;
import android.view.autofill.IAutofillWindowPresenter;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.widget.RemoteViews;
-
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -198,7 +195,6 @@ import com.android.server.autofill.ui.InlineFillUi;
import com.android.server.autofill.ui.PendingUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
-
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -222,18 +218,21 @@ import java.util.function.Function;
/**
* A session for a given activity.
*
- * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track
- * of the current {@link ViewState} to display the appropriate UI.
+ * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track of
+ * the current {@link ViewState} to display the appropriate UI.
*
- * <p>Although the autofill requests and callbacks are stateless from the service's point of
- * view, we need to keep state in the framework side for cases such as authentication. For
- * example, when service return a {@link FillResponse} that contains all the fields needed
- * to fill the activity but it requires authentication first, that response need to be held
- * until the user authenticates or it times out.
+ * <p>Although the autofill requests and callbacks are stateless from the service's point of view,
+ * we need to keep state in the framework side for cases such as authentication. For example, when
+ * service return a {@link FillResponse} that contains all the fields needed to fill the activity
+ * but it requires authentication first, that response need to be held until the user authenticates
+ * or it times out.
*/
-final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener,
- AutoFillUI.AutoFillUiCallback, ValueFinder,
- RemoteFieldClassificationService.FieldClassificationServiceCallbacks {
+final class Session
+ implements RemoteFillService.FillServiceCallbacks,
+ ViewState.Listener,
+ AutoFillUI.AutoFillUiCallback,
+ ValueFinder,
+ RemoteFieldClassificationService.FieldClassificationServiceCallbacks {
private static final String TAG = "AutofillSession";
// This should never be true in production. This is only for local debugging.
@@ -257,6 +256,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private final AutofillManagerServiceImpl mService;
private final Handler mHandler;
private final AutoFillUI mUi;
+
/**
* Context associated with the session, it has the same {@link Context#getDisplayId() displayId}
* of the activity being autofilled.
@@ -286,14 +286,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/** Session is destroyed and removed from the manager service. */
public static final int STATE_REMOVED = 3;
- @IntDef(prefix = { "STATE_" }, value = {
- STATE_UNKNOWN,
- STATE_ACTIVE,
- STATE_FINISHED,
- STATE_REMOVED
- })
+ @IntDef(
+ prefix = {"STATE_"},
+ value = {STATE_UNKNOWN, STATE_ACTIVE, STATE_FINISHED, STATE_REMOVED})
@Retention(RetentionPolicy.SOURCE)
- @interface SessionState{}
+ @interface SessionState {}
@GuardedBy("mLock")
private final SessionFlags mSessionFlags;
@@ -318,7 +315,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
public final int mFlags;
@GuardedBy("mLock")
- @NonNull private IBinder mActivityToken;
+ @NonNull
+ private IBinder mActivityToken;
/** The app activity that's being autofilled */
@NonNull private final ComponentName mComponentName;
@@ -341,11 +339,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* autofill.
*/
@GuardedBy("mLock")
- @Nullable private Pair<Integer, InlineSuggestionsRequest> mLastInlineSuggestionsRequest;
+ @Nullable
+ private Pair<Integer, InlineSuggestionsRequest> mLastInlineSuggestionsRequest;
- /**
- * Id of the View currently being displayed.
- */
+ /** Id of the View currently being displayed. */
@GuardedBy("mLock")
private @Nullable AutofillId mCurrentViewId;
@@ -363,8 +360,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*
* <p>Only {@code null} when the session is for augmented autofill only.
*/
- @Nullable
- private final RemoteFillService mRemoteFillService;
+ @Nullable private final RemoteFillService mRemoteFillService;
/**
* With the credman integration, Autofill Framework handles two types of autofill flows -
@@ -379,8 +375,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* service the session was initially created with, the secondary provider handler will contain
* the remaining autofill service.
*/
- @Nullable
- private final SecondaryProviderHandler mSecondaryProviderHandler;
+ @Nullable private final SecondaryProviderHandler mSecondaryProviderHandler;
@GuardedBy("mLock")
private SparseArray<FillResponse> mResponses;
@@ -395,9 +390,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private ArrayList<FillContext> mContexts;
- /**
- * Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}.
- */
+ /** Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}. */
private boolean mHasCallback;
/** Whether the session has credential manager provider as the primary provider. */
@@ -426,32 +419,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private PendingUi mPendingSaveUi;
- /**
- * List of dataset ids selected by the user.
- */
+ /** List of dataset ids selected by the user. */
@GuardedBy("mLock")
private ArrayList<String> mSelectedDatasetIds;
- /**
- * When the session started (using elapsed time since boot).
- */
+ /** When the session started (using elapsed time since boot). */
private final long mStartTime;
- /**
- * Count of FillRequests in the session.
- */
+ /** Count of FillRequests in the session. */
private int mRequestCount;
/**
- * Starting timestamp of latency logger.
- * This is set when Session created or when the view is reset.
+ * Starting timestamp of latency logger. This is set when Session created or when the view is
+ * reset.
*/
@GuardedBy("mLock")
private long mLatencyBaseTime;
- /**
- * When the UI was shown for the first time (using elapsed time since boot).
- */
+ /** When the UI was shown for the first time (using elapsed time since boot). */
@GuardedBy("mLock")
private long mUiShownTime;
@@ -475,15 +460,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private final LocalLog mWtfHistory;
- /**
- * Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id.
- */
+ /** Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id. */
@GuardedBy("mLock")
private final SparseArray<LogMaker> mRequestLogs = new SparseArray<>(1);
- /**
- * Destroys the augmented Autofill UI.
- */
+ /** Destroys the augmented Autofill UI. */
// TODO(b/123099468): this runnable is called when the Autofill session is destroyed, the
// main reason being the cases where user tap HOME.
// Right now it's completely destroying the UI, but we need to decide whether / how to
@@ -494,13 +475,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Nullable
private Runnable mAugmentedAutofillDestroyer;
- /**
- * List of {@link MetricsEvent#AUTOFILL_AUGMENTED_REQUEST} metrics.
- */
+ /** List of {@link MetricsEvent#AUTOFILL_AUGMENTED_REQUEST} metrics. */
@GuardedBy("mLock")
private ArrayList<LogMaker> mAugmentedRequestsLogs;
-
/**
* List of autofill ids of autofillable fields present in the AssistStructure that can be used
* to trigger new augmented autofill requests (because the "standard" service was not interested
@@ -509,23 +487,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private ArrayList<AutofillId> mAugmentedAutofillableIds;
- @NonNull
- final AutofillInlineSessionController mInlineSessionController;
+ @NonNull final AutofillInlineSessionController mInlineSessionController;
- /**
- * Receiver of assist data from the app's {@link Activity}.
- */
+ /** Receiver of assist data from the app's {@link Activity}. */
private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
- /**
- * Receiver of assist data for pcc purpose
- */
+ /** Receiver of assist data for pcc purpose */
private final PccAssistDataReceiverImpl mPccAssistReceiver = new PccAssistDataReceiverImpl();
private final ClassificationState mClassificationState = new ClassificationState();
- @Nullable
- private final ComponentName mCredentialAutofillService;
+ @Nullable private final ComponentName mCredentialAutofillService;
// TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a
// new one per Session.
@@ -547,7 +519,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.v(TAG, "mDelayedFillBroadcastReceiver delayed fill action received");
synchronized (mLock) {
int requestId = intent.getIntExtra(EXTRA_REQUEST_ID, 0);
- FillResponse response = intent.getParcelableExtra(EXTRA_FILL_RESPONSE, android.service.autofill.FillResponse.class);
+ FillResponse response =
+ intent.getParcelableExtra(
+ EXTRA_FILL_RESPONSE,
+ android.service.autofill.FillResponse.class);
mFillRequestEventLogger.maybeSetRequestTriggerReason(
TRIGGER_REASON_RETRIGGER);
mAssistReceiver.processDelayedFillLocked(requestId, response);
@@ -575,28 +550,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private SessionCommittedEventLogger mSessionCommittedEventLogger;
- /**
- * Fill dialog request would likely be sent slightly later.
- */
+ /** Fill dialog request would likely be sent slightly later. */
@NonNull
@GuardedBy("mLock")
private boolean mPreviouslyFillDialogPotentiallyStarted;
/**
- * Keeps track of if the user entered view, this is used to
- * distinguish Fill Request that did not have user interaction
- * with ones that did.
+ * Keeps track of if the user entered view, this is used to distinguish Fill Request that did
+ * not have user interaction with ones that did.
*
- * This is set to true when entering view - after FillDialog FillRequest
- * or on plain user tap.
+ * <p>This is set to true when entering view - after FillDialog FillRequest or on plain user
+ * tap.
*/
@NonNull
@GuardedBy("mLock")
private boolean mLogViewEntered;
/**
- * Keeps the fill dialog trigger ids of the last response. This invalidates
- * the trigger ids of the previous response.
+ * Keeps the fill dialog trigger ids of the last response. This invalidates the trigger ids of
+ * the previous response.
*/
@Nullable
@GuardedBy("mLock")
@@ -662,9 +634,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return false;
}
- /**
- * Collection of flags/booleans that helps determine Session behaviors.
- */
+ /** Collection of flags/booleans that helps determine Session behaviors. */
private final class SessionFlags {
/** Whether autofill is disabled by the service */
private boolean mAutofillDisabled;
@@ -695,31 +665,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub {
@GuardedBy("mLock")
private boolean mWaitForInlineRequest;
+
@GuardedBy("mLock")
private InlineSuggestionsRequest mPendingInlineSuggestionsRequest;
+
@GuardedBy("mLock")
private FillRequest mPendingFillRequest;
+
@GuardedBy("mLock")
private FillRequest mLastFillRequest;
- @Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(ViewState viewState,
- boolean isInlineRequest) {
+ @Nullable
+ Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(
+ ViewState viewState, boolean isInlineRequest) {
mPendingFillRequest = null;
mWaitForInlineRequest = isInlineRequest;
mPendingInlineSuggestionsRequest = null;
if (isInlineRequest) {
WeakReference<AssistDataReceiverImpl> assistDataReceiverWeakReference =
- new WeakReference<AssistDataReceiverImpl>(this);
+ new WeakReference<AssistDataReceiverImpl>(this);
WeakReference<ViewState> viewStateWeakReference =
- new WeakReference<ViewState>(viewState);
- return new InlineSuggestionRequestConsumer(assistDataReceiverWeakReference,
- viewStateWeakReference);
+ new WeakReference<ViewState>(viewState);
+ return new InlineSuggestionRequestConsumer(
+ assistDataReceiverWeakReference, viewStateWeakReference);
}
return null;
}
- void handleInlineSuggestionRequest(InlineSuggestionsRequest inlineSuggestionsRequest,
- ViewState viewState) {
+ void handleInlineSuggestionRequest(
+ InlineSuggestionsRequest inlineSuggestionsRequest, ViewState viewState) {
if (sVerbose) {
Slog.v(TAG, "handleInlineSuggestionRequest(): inline suggestion request received");
}
@@ -738,8 +712,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
void maybeRequestFillLocked() {
if (mPendingFillRequest == null) {
if (sVerbose) {
- Slog.v(TAG, "maybeRequestFillLocked(): cancelling calling fill request "
- + "due to empty pending fill request");
+ Slog.v(
+ TAG,
+ "maybeRequestFillLocked(): cancelling calling fill request "
+ + "due to empty pending fill request");
}
return;
}
@@ -748,27 +724,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (mWaitForInlineRequest) {
if (mPendingInlineSuggestionsRequest == null) {
if (sVerbose) {
- Slog.v(TAG, "maybeRequestFillLocked(): cancelling calling fill request "
- + "due to waiting for inline request and pending inline request is "
- + "currently empty");
+ Slog.v(
+ TAG,
+ "maybeRequestFillLocked(): cancelling calling fill request due to"
+ + " waiting for inline request and pending inline request is"
+ + " currently empty");
}
return;
}
if (sVerbose) {
- Slog.v(TAG, "maybeRequestFillLocked(): adding inline request to pending "
- + "fill request");
+ Slog.v(
+ TAG,
+ "maybeRequestFillLocked(): adding inline request to pending "
+ + "fill request");
}
- mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
- mPendingFillRequest.getFillContexts(),
- mPendingFillRequest.getHints(),
- mPendingFillRequest.getClientState(),
- mPendingFillRequest.getFlags(),
- mPendingInlineSuggestionsRequest,
- mPendingFillRequest.getDelayedFillIntentSender());
+ mPendingFillRequest =
+ new FillRequest(
+ mPendingFillRequest.getId(),
+ mPendingFillRequest.getFillContexts(),
+ mPendingFillRequest.getHints(),
+ mPendingFillRequest.getClientState(),
+ mPendingFillRequest.getFlags(),
+ mPendingInlineSuggestionsRequest,
+ mPendingFillRequest.getDelayedFillIntentSender());
} else {
if (sVerbose) {
- Slog.v(TAG, "maybeRequestFillLocked(): not adding inline request to pending "
- + "fill request");
+ Slog.v(
+ TAG,
+ "maybeRequestFillLocked(): not adding inline request to pending "
+ + "fill request");
}
}
@@ -780,19 +764,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
&& mSecondaryProviderHandler != null) {
Slog.v(TAG, "Requesting fill response to secondary provider.");
if (!mIsPrimaryCredential) {
- mPendingFillRequest = addCredentialManagerDataToClientState(
- mPendingFillRequest,
- mPendingInlineSuggestionsRequest, id);
+ mPendingFillRequest =
+ addCredentialManagerDataToClientState(
+ mPendingFillRequest, mPendingInlineSuggestionsRequest, id);
}
- mSecondaryProviderHandler.onFillRequest(mPendingFillRequest,
- mPendingFillRequest.getFlags(), mClient.asBinder());
+ mSecondaryProviderHandler.onFillRequest(
+ mPendingFillRequest, mPendingFillRequest.getFlags(), mClient.asBinder());
} else if (mRemoteFillService != null) {
if (mIsPrimaryCredential) {
- mPendingFillRequest = addCredentialManagerDataToClientState(
- mPendingFillRequest,
- mPendingInlineSuggestionsRequest, id);
- mRemoteFillService.onFillCredentialRequest(mPendingFillRequest,
- mClient.asBinder());
+ mPendingFillRequest =
+ addCredentialManagerDataToClientState(
+ mPendingFillRequest, mPendingInlineSuggestionsRequest, id);
+ mRemoteFillService.onFillCredentialRequest(
+ mPendingFillRequest, mClient.asBinder());
} else {
mRemoteFillService.onFillRequest(mPendingFillRequest);
}
@@ -813,8 +797,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Override
public void onHandleAssistData(Bundle resultData) throws RemoteException {
if (mRemoteFillService == null) {
- wtf(null, "onHandleAssistData() called without a remote service. "
- + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
+ wtf(
+ null,
+ "onHandleAssistData() called without a remote service. "
+ + "mForAugmentedAutofillOnly: %s",
+ mSessionFlags.mAugmentedAutofillOnly);
return;
}
// Keeps to prevent it is cleared on multiple threads.
@@ -824,7 +811,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
- final AssistStructure structure = resultData.getParcelable(ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class);
+ final AssistStructure structure =
+ resultData.getParcelable(
+ ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class);
if (structure == null) {
Slog.e(TAG, "No assist structure - app might have crashed providing it");
return;
@@ -852,13 +841,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
try {
structure.ensureDataForAutofill();
} catch (RuntimeException e) {
- wtf(e, "Exception lazy loading assist structure for %s: %s",
- structure.getActivityComponent(), e);
+ wtf(
+ e,
+ "Exception lazy loading assist structure for %s: %s",
+ structure.getActivityComponent(),
+ e);
return;
}
- final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure,
- /* autofillableOnly= */false);
+ final ArrayList<AutofillId> ids =
+ Helper.getAutofillIds(structure, /* autofillableOnly= */ false);
for (int i = 0; i < ids.size(); i++) {
ids.get(i).setSessionId(Session.this.id);
}
@@ -868,8 +860,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (mCompatMode) {
// Sanitize URL bar, if needed
- final String[] urlBarIds = mService.getUrlBarResourceIdsForCompatMode(
- mComponentName.getPackageName());
+ final String[] urlBarIds =
+ mService.getUrlBarResourceIdsForCompatMode(
+ mComponentName.getPackageName());
if (sDebug) {
Slog.d(TAG, "url_bars in compat mode: " + Arrays.toString(urlBarIds));
}
@@ -878,11 +871,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (mUrlBar != null) {
final AutofillId urlBarId = mUrlBar.getAutofillId();
if (sDebug) {
- Slog.d(TAG, "Setting urlBar as id=" + urlBarId + " and domain "
- + mUrlBar.getWebDomain());
+ Slog.d(
+ TAG,
+ "Setting urlBar as id="
+ + urlBarId
+ + " and domain "
+ + mUrlBar.getWebDomain());
}
- final ViewState viewState = new ViewState(urlBarId, Session.this,
- ViewState.STATE_URL_BAR, mIsPrimaryCredential);
+ final ViewState viewState =
+ new ViewState(
+ urlBarId,
+ Session.this,
+ ViewState.STATE_URL_BAR,
+ mIsPrimaryCredential);
mViewStates.put(urlBarId, viewState);
}
}
@@ -907,11 +908,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final List<String> hints = getTypeHintsForProvider();
mDelayedFillPendingIntent = createPendingIntent(requestId);
- request = new FillRequest(requestId, contexts, hints, mClientState, flags,
- /*inlineSuggestionsRequest=*/ null,
- /*delayedFillIntentSender=*/ mDelayedFillPendingIntent == null
- ? null
- : mDelayedFillPendingIntent.getIntentSender());
+ request =
+ new FillRequest(
+ requestId,
+ contexts,
+ hints,
+ mClientState,
+ flags,
+ /* inlineSuggestionsRequest= */ null,
+ /* delayedFillIntentSender= */ mDelayedFillPendingIntent == null
+ ? null
+ : mDelayedFillPendingIntent.getIntentSender());
mPendingFillRequest = request;
maybeRequestFillLocked();
@@ -930,39 +937,49 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
void processDelayedFillLocked(int requestId, FillResponse response) {
if (mLastFillRequest != null && requestId == mLastFillRequest.getId()) {
- Slog.v(TAG, "processDelayedFillLocked: "
- + "calling onFillRequestSuccess with new response");
- onFillRequestSuccess(requestId, response,
- mService.getServicePackageName(), mLastFillRequest.getFlags());
+ Slog.v(
+ TAG,
+ "processDelayedFillLocked: "
+ + "calling onFillRequestSuccess with new response");
+ onFillRequestSuccess(
+ requestId,
+ response,
+ mService.getServicePackageName(),
+ mLastFillRequest.getFlags());
}
}
}
- private FillRequest addCredentialManagerDataToClientState(FillRequest pendingFillRequest,
- InlineSuggestionsRequest pendingInlineSuggestionsRequest, int sessionId) {
+ private FillRequest addCredentialManagerDataToClientState(
+ FillRequest pendingFillRequest,
+ InlineSuggestionsRequest pendingInlineSuggestionsRequest,
+ int sessionId) {
if (pendingFillRequest.getClientState() == null) {
- pendingFillRequest = new FillRequest(pendingFillRequest.getId(),
- pendingFillRequest.getFillContexts(),
- pendingFillRequest.getHints(),
- new Bundle(),
- pendingFillRequest.getFlags(),
- pendingInlineSuggestionsRequest,
- pendingFillRequest.getDelayedFillIntentSender());
+ pendingFillRequest =
+ new FillRequest(
+ pendingFillRequest.getId(),
+ pendingFillRequest.getFillContexts(),
+ pendingFillRequest.getHints(),
+ new Bundle(),
+ pendingFillRequest.getFlags(),
+ pendingInlineSuggestionsRequest,
+ pendingFillRequest.getDelayedFillIntentSender());
}
pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, sessionId);
pendingFillRequest.getClientState().putInt(REQUEST_ID_KEY, pendingFillRequest.getId());
- ResultReceiver resultReceiver = constructCredentialManagerCallback(
- pendingFillRequest.getId());
- pendingFillRequest.getClientState().putParcelable(
- CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER, resultReceiver);
+ ResultReceiver resultReceiver =
+ constructCredentialManagerCallback(pendingFillRequest.getId());
+ pendingFillRequest
+ .getClientState()
+ .putParcelable(CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER, resultReceiver);
return pendingFillRequest;
}
/**
- * Get the list of valid autofill hint types from Device flags
- * Returns empty list if PCC is off or no types available
- */
+ * Get the list of valid autofill hint types from Device flags Returns empty list if PCC is off
+ * or no types available
+ */
private List<String> getTypeHintsForProvider() {
if (!mService.isPccClassificationEnabled()) {
return Collections.EMPTY_LIST;
@@ -978,9 +995,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return List.of(typeHints.split(PCC_HINTS_DELIMITER));
}
- /**
- * Assist Data Receiver for PCC
- */
+ /** Assist Data Receiver for PCC */
private final class PccAssistDataReceiverImpl extends IAssistDataReceiver.Stub {
@GuardedBy("mLock")
@@ -998,7 +1013,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
new WeakReference<>(Session.this);
remoteFieldClassificationService.onFieldClassificationRequest(
mClassificationState.mPendingFieldClassificationRequest,
- fieldClassificationServiceCallbacksWeakRef);
+ fieldClassificationServiceCallbacksWeakRef);
}
mClassificationState.onFieldClassificationRequestSent();
}
@@ -1006,26 +1021,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Override
public void onHandleAssistData(Bundle resultData) throws RemoteException {
// TODO: add a check if pcc field classification service is present
- final AssistStructure structure = resultData.getParcelable(ASSIST_KEY_STRUCTURE,
- android.app.assist.AssistStructure.class);
+ final AssistStructure structure =
+ resultData.getParcelable(
+ ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class);
if (structure == null) {
- Slog.e(TAG, "No assist structure for pcc detection - "
- + "app might have crashed providing it");
+ Slog.e(
+ TAG,
+ "No assist structure for pcc detection - "
+ + "app might have crashed providing it");
return;
}
final Bundle receiverExtras = resultData.getBundle(ASSIST_KEY_RECEIVER_EXTRAS);
if (receiverExtras == null) {
- Slog.e(TAG, "No receiver extras for pcc detection - "
- + "app might have crashed providing it");
+ Slog.e(
+ TAG,
+ "No receiver extras for pcc detection - "
+ + "app might have crashed providing it");
return;
}
final int requestId = receiverExtras.getInt(EXTRA_REQUEST_ID);
if (sVerbose) {
- Slog.v(TAG, "New structure for PCC Detection: requestId " + requestId + ": "
- + structure);
+ Slog.v(
+ TAG,
+ "New structure for PCC Detection: requestId "
+ + requestId
+ + ": "
+ + structure);
}
synchronized (mLock) {
@@ -1037,13 +1061,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
try {
structure.ensureDataForAutofill();
} catch (RuntimeException e) {
- wtf(e, "Exception lazy loading assist structure for %s: %s",
- structure.getActivityComponent(), e);
+ wtf(
+ e,
+ "Exception lazy loading assist structure for %s: %s",
+ structure.getActivityComponent(),
+ e);
return;
}
- final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure,
- /* autofillableOnly= */false);
+ final ArrayList<AutofillId> ids =
+ Helper.getAutofillIds(structure, /* autofillableOnly= */ false);
for (int i = 0; i < ids.size(); i++) {
ids.get(i).setSessionId(Session.this.id);
}
@@ -1066,13 +1093,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
PendingIntent pendingIntent;
final long identity = Binder.clearCallingIdentity();
try {
- Intent intent = new Intent(ACTION_DELAYED_FILL).setPackage("android")
- .putExtra(EXTRA_REQUEST_ID, requestId);
- pendingIntent = PendingIntent.getBroadcast(
- mContext, this.id, intent,
- PendingIntent.FLAG_MUTABLE
- | PendingIntent.FLAG_ONE_SHOT
- | PendingIntent.FLAG_CANCEL_CURRENT);
+ Intent intent =
+ new Intent(ACTION_DELAYED_FILL)
+ .setPackage("android")
+ .putExtra(EXTRA_REQUEST_ID, requestId);
+ pendingIntent =
+ PendingIntent.getBroadcast(
+ mContext,
+ this.id,
+ intent,
+ PendingIntent.FLAG_MUTABLE
+ | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_CANCEL_CURRENT);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1113,9 +1145,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- /**
- * Returns the ids of all entries in {@link #mViewStates} in the same order.
- */
+ /** Returns the ids of all entries in {@link #mViewStates} in the same order. */
@GuardedBy("mLock")
private AutofillId[] getIdsOfAllViewStatesLocked() {
final int numViewState = mViewStates.size();
@@ -1164,8 +1194,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
- * <p>Gets the value of a field, using either the {@code viewStates} or the {@code mContexts},
- * or {@code null} when not found on either of them.
+ * Gets the value of a field, using either the {@code viewStates} or the {@code mContexts}, or
+ * {@code null} when not found on either of them.
*/
@GuardedBy("mLock")
@Nullable
@@ -1180,16 +1210,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final ArrayList<Session> previousSessions = mService.getPreviousSessionsLocked(this);
if (previousSessions != null) {
if (sDebug) {
- Slog.d(TAG, "findValueLocked(): looking on " + previousSessions.size()
- + " previous sessions for autofillId " + autofillId);
+ Slog.d(
+ TAG,
+ "findValueLocked(): looking on "
+ + previousSessions.size()
+ + " previous sessions for autofillId "
+ + autofillId);
}
for (int i = 0; i < previousSessions.size(); i++) {
final Session previousSession = previousSessions.get(i);
- final AutofillValue previousValue = previousSession
- .findValueFromThisSessionOnlyLocked(autofillId);
+ final AutofillValue previousValue =
+ previousSession.findValueFromThisSessionOnlyLocked(autofillId);
if (previousValue != null) {
- return getSanitizedValue(createSanitizers(previousSession.getSaveInfoLocked()),
- autofillId, previousValue);
+ return getSanitizedValue(
+ createSanitizers(previousSession.getSaveInfoLocked()),
+ autofillId,
+ previousValue);
}
}
}
@@ -1212,16 +1248,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
AutofillValue candidateSaveValue = state.getCandidateSaveValue();
if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) {
if (sDebug) {
- Slog.d(TAG, "findValueLocked(): current value for " + autofillId
- + " is empty, using candidateSaveValue instead.");
+ Slog.d(
+ TAG,
+ "findValueLocked(): current value for "
+ + autofillId
+ + " is empty, using candidateSaveValue instead.");
}
return candidateSaveValue;
}
}
if (value == null) {
if (sDebug) {
- Slog.d(TAG, "findValueLocked(): no current value for " + autofillId
- + ", checking value from previous fill contexts");
+ Slog.d(
+ TAG,
+ "findValueLocked(): no current value for "
+ + autofillId
+ + ", checking value from previous fill contexts");
value = getValueFromContextsLocked(autofillId);
}
}
@@ -1231,17 +1273,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/**
* Updates values of the nodes in the context's structure so that:
*
- * - proper node is focused
- * - autofillValue is sent back to service when it was previously autofilled
- * - autofillValue is sent in the view used to force a request
+ * <p>- proper node is focused - autofillValue is sent back to service when it was previously
+ * autofilled - autofillValue is sent in the view used to force a request
*
* @param fillContext The context to be filled
* @param flags The flags that started the session
*/
@GuardedBy("mLock")
private void fillContextWithAllowedValuesLocked(@NonNull FillContext fillContext, int flags) {
- final ViewNode[] nodes = fillContext
- .findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked());
+ final ViewNode[] nodes =
+ fillContext.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked());
final int numViewState = mViewStates.size();
for (int i = 0; i < numViewState; i++) {
@@ -1250,7 +1291,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final ViewNode node = nodes[i];
if (node == null) {
if (sVerbose) {
- Slog.v(TAG,
+ Slog.v(
+ TAG,
"fillContextWithAllowedValuesLocked(): no node for " + viewState.id);
}
continue;
@@ -1277,14 +1319,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- /**
- * Cancels the last request sent to the {@link #mRemoteFillService}.
- */
+ /** Cancels the last request sent to the {@link #mRemoteFillService}. */
@GuardedBy("mLock")
private void cancelCurrentRequestLocked() {
if (mRemoteFillService == null) {
- wtf(null, "cancelCurrentRequestLocked() called without a remote service. "
- + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
+ wtf(
+ null,
+ "cancelCurrentRequestLocked() called without a remote service. "
+ + "mForAugmentedAutofillOnly: %s",
+ mSessionFlags.mAugmentedAutofillOnly);
return;
}
final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
@@ -1318,21 +1361,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private Optional<Integer> requestNewFillResponseLocked(
@NonNull ViewState viewState, int newState, int flags) {
boolean isSecondary = shouldRequestSecondaryProvider(flags);
- final FillResponse existingResponse = isSecondary
- ? viewState.getSecondaryResponse() : viewState.getResponse();
+ final FillResponse existingResponse =
+ isSecondary ? viewState.getSecondaryResponse() : viewState.getResponse();
mFillRequestEventLogger.startLogForNewRequest();
mRequestCount++;
mFillRequestEventLogger.maybeSetAppPackageUid(uid);
mFillRequestEventLogger.maybeSetFlags(mFlags);
- if(mPreviouslyFillDialogPotentiallyStarted) {
+ if (mPreviouslyFillDialogPotentiallyStarted) {
mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_PRE_TRIGGER);
} else {
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
mFillRequestEventLogger.maybeSetRequestTriggerReason(
TRIGGER_REASON_EXPLICITLY_REQUESTED);
} else {
- mFillRequestEventLogger.maybeSetRequestTriggerReason(
- TRIGGER_REASON_NORMAL_TRIGGER);
+ mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_NORMAL_TRIGGER);
}
}
if (existingResponse != null) {
@@ -1348,9 +1390,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mSessionState = STATE_ACTIVE;
if (mSessionFlags.mAugmentedAutofillOnly || mRemoteFillService == null) {
if (sVerbose) {
- Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead "
- + "(mForAugmentedAutofillOnly=" + mSessionFlags.mAugmentedAutofillOnly
- + ", flags=" + flags + ")");
+ Slog.v(
+ TAG,
+ "requestNewFillResponse(): triggering augmented autofill instead "
+ + "(mForAugmentedAutofillOnly="
+ + mSessionFlags.mAugmentedAutofillOnly
+ + ", flags="
+ + flags
+ + ")");
}
mSessionFlags.mAugmentedAutofillOnly = true;
mFillRequestEventLogger.maybeSetRequestId(AUGMENTED_AUTOFILL_REQUEST_ID);
@@ -1365,16 +1412,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Create a metrics log for the request
final int ordinal = mRequestLogs.size() + 1;
- final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_REQUEST)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL, ordinal);
+ final LogMaker log =
+ newLogMaker(MetricsEvent.AUTOFILL_REQUEST)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL, ordinal);
if (flags != 0) {
log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags);
}
mRequestLogs.put(requestId, log);
if (sVerbose) {
- Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId=" + requestId
- + ", flags=" + flags);
+ Slog.v(
+ TAG,
+ "Requesting structure for request #"
+ + ordinal
+ + " ,requestId="
+ + requestId
+ + ", flags="
+ + flags);
}
boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
mFillRequestEventLogger.maybeSetRequestId(requestId);
@@ -1406,11 +1460,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// is also not focused.
final RemoteInlineSuggestionRenderService remoteRenderService =
mService.getRemoteInlineSuggestionRenderServiceLocked();
- if (mSessionFlags.mInlineSupportedByService && remoteRenderService != null
- && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) {
+ if (mSessionFlags.mInlineSupportedByService
+ && remoteRenderService != null
+ && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) {
Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
- mAssistReceiver.newAutofillRequestLocked(viewState,
- /* isInlineRequest= */ true);
+ mAssistReceiver.newAutofillRequestLocked(
+ viewState, /* isInlineRequest= */ true);
if (inlineSuggestionsRequestConsumer != null) {
final int requestIdCopy = requestId;
final AutofillId focusedId = mCurrentViewId;
@@ -1423,8 +1478,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
requestIdCopy,
inlineSuggestionsRequestConsumer,
focusedId);
- RemoteCallback inlineSuggestionRendorInfoCallback = new RemoteCallback(
- inlineSuggestionRendorInfoCallbackOnResultListener, mHandler);
+ RemoteCallback inlineSuggestionRendorInfoCallback =
+ new RemoteCallback(
+ inlineSuggestionRendorInfoCallbackOnResultListener, mHandler);
remoteRenderService.getInlineSuggestionsRendererInfo(
inlineSuggestionRendorInfoCallback);
@@ -1465,8 +1521,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
receiverExtras.putInt(EXTRA_REQUEST_ID, requestId);
final long identity = Binder.clearCallingIdentity();
try {
- if (!ActivityTaskManager.getService().requestAutofillData(mPccAssistReceiver,
- receiverExtras, mActivityToken, flags)) {
+ if (!ActivityTaskManager.getService()
+ .requestAutofillData(
+ mPccAssistReceiver, receiverExtras, mActivityToken, flags)) {
Slog.w(TAG, "failed to request autofill data for " + mActivityToken);
}
} finally {
@@ -1483,8 +1540,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
receiverExtras.putInt(EXTRA_REQUEST_ID, requestId);
final long identity = Binder.clearCallingIdentity();
try {
- if (!ActivityTaskManager.getService().requestAutofillData(mAssistReceiver,
- receiverExtras, mActivityToken, flags)) {
+ if (!ActivityTaskManager.getService()
+ .requestAutofillData(
+ mAssistReceiver, receiverExtras, mActivityToken, flags)) {
Slog.w(TAG, "failed to request autofill data for " + mActivityToken);
}
} finally {
@@ -1495,13 +1553,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui,
- @NonNull Context context, @NonNull Handler handler, int userId, @NonNull Object lock,
- int sessionId, int taskId, int uid, @NonNull IBinder activityToken,
- @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory,
- @NonNull LocalLog wtfHistory, @Nullable ComponentName serviceComponentName,
- @NonNull ComponentName componentName, boolean compatMode,
- boolean bindInstantServiceAllowed, boolean forAugmentedAutofillOnly, int flags,
+ Session(
+ @NonNull AutofillManagerServiceImpl service,
+ @NonNull AutoFillUI ui,
+ @NonNull Context context,
+ @NonNull Handler handler,
+ int userId,
+ @NonNull Object lock,
+ int sessionId,
+ int taskId,
+ int uid,
+ @NonNull IBinder activityToken,
+ @NonNull IBinder client,
+ boolean hasCallback,
+ @NonNull LocalLog uiLatencyHistory,
+ @NonNull LocalLog wtfHistory,
+ @Nullable ComponentName serviceComponentName,
+ @NonNull ComponentName componentName,
+ boolean compatMode,
+ boolean bindInstantServiceAllowed,
+ boolean forAugmentedAutofillOnly,
+ int flags,
@NonNull InputMethodManagerInternal inputMethodManagerInternal,
boolean isPrimaryCredential) {
if (sessionId < 0) {
@@ -1533,22 +1605,40 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
primaryServiceComponentName = serviceComponentName;
secondaryServiceComponentName = mCredentialAutofillService;
}
- Slog.v(TAG, "Primary service component name: " + primaryServiceComponentName
- + ", secondary service component name: " + secondaryServiceComponentName);
-
- mRemoteFillService = primaryServiceComponentName == null ? null
- : new RemoteFillService(context, primaryServiceComponentName, userId, this,
- bindInstantServiceAllowed, mCredentialAutofillService);
- mSecondaryProviderHandler = secondaryServiceComponentName == null ? null
- : new SecondaryProviderHandler(context, userId, bindInstantServiceAllowed,
- this::onSecondaryFillResponse, secondaryServiceComponentName,
- mCredentialAutofillService);
+ Slog.v(
+ TAG,
+ "Primary service component name: "
+ + primaryServiceComponentName
+ + ", secondary service component name: "
+ + secondaryServiceComponentName);
+
+ mRemoteFillService =
+ primaryServiceComponentName == null
+ ? null
+ : new RemoteFillService(
+ context,
+ primaryServiceComponentName,
+ userId,
+ this,
+ bindInstantServiceAllowed,
+ mCredentialAutofillService);
+ mSecondaryProviderHandler =
+ secondaryServiceComponentName == null
+ ? null
+ : new SecondaryProviderHandler(
+ context,
+ userId,
+ bindInstantServiceAllowed,
+ this::onSecondaryFillResponse,
+ secondaryServiceComponentName,
+ mCredentialAutofillService);
mActivityToken = activityToken;
mHasCallback = hasCallback;
mUiLatencyHistory = uiLatencyHistory;
mWtfHistory = wtfHistory;
- int displayId = LocalServices.getService(ActivityTaskManagerInternal.class)
- .getDisplayId(activityToken);
+ int displayId =
+ LocalServices.getService(ActivityTaskManagerInternal.class)
+ .getDisplayId(activityToken);
mContext = Helper.getDisplayContext(context, displayId);
mComponentName = componentName;
mCompatMode = compatMode;
@@ -1557,8 +1647,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mStartTime = SystemClock.elapsedRealtime();
mLatencyBaseTime = mStartTime;
mRequestCount = 0;
- mPresentationStatsEventLogger = PresentationStatsEventLogger.createPresentationLog(
- sessionId, uid, mLatencyBaseTime);
+ mPresentationStatsEventLogger =
+ PresentationStatsEventLogger.createPresentationLog(
+ sessionId, uid, mLatencyBaseTime);
mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId);
mFillResponseEventLogger = FillResponseEventLogger.forSessionId(sessionId);
mSessionCommittedEventLogger = SessionCommittedEventLogger.forSessionId(sessionId);
@@ -1574,33 +1665,39 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
setClientLocked(client);
}
- mInlineSessionController = new AutofillInlineSessionController(inputMethodManagerInternal,
- userId, componentName, handler, mLock,
- new InlineFillUi.InlineUiEventCallback() {
- @Override
- public void notifyInlineUiShown(AutofillId autofillId) {
- notifyFillUiShown(autofillId);
- }
+ mInlineSessionController =
+ new AutofillInlineSessionController(
+ inputMethodManagerInternal,
+ userId,
+ componentName,
+ handler,
+ mLock,
+ new InlineFillUi.InlineUiEventCallback() {
+ @Override
+ public void notifyInlineUiShown(AutofillId autofillId) {
+ notifyFillUiShown(autofillId);
+ }
- @Override
- public void notifyInlineUiHidden(AutofillId autofillId) {
- notifyFillUiHidden(autofillId);
- }
- });
+ @Override
+ public void notifyInlineUiHidden(AutofillId autofillId) {
+ notifyFillUiHidden(autofillId);
+ }
+ });
- mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
+ mMetricsLogger.write(
+ newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
mLogViewEntered = false;
}
private ComponentName getCredentialAutofillService(Context context) {
ComponentName componentName = null;
- String credentialManagerAutofillCompName = context.getResources().getString(
- R.string.config_defaultCredentialManagerAutofillService);
+ String credentialManagerAutofillCompName =
+ context.getResources()
+ .getString(R.string.config_defaultCredentialManagerAutofillService);
if (credentialManagerAutofillCompName != null
&& !credentialManagerAutofillCompName.isEmpty()) {
- componentName = ComponentName.unflattenFromString(
- credentialManagerAutofillCompName);
+ componentName = ComponentName.unflattenFromString(credentialManagerAutofillCompName);
}
if (componentName == null) {
Slog.w(TAG, "Invalid CredentialAutofillService");
@@ -1614,7 +1711,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* @return The activity token
*/
@GuardedBy("mLock")
- @NonNull IBinder getActivityTokenLocked() {
+ @NonNull
+ IBinder getActivityTokenLocked() {
return mActivityToken;
}
@@ -1627,8 +1725,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
void switchActivity(@NonNull IBinder newActivity, @NonNull IBinder newClient) {
synchronized (mLock) {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#switchActivity() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#switchActivity() rejected - session: "
+ + id
+ + " destroyed");
return;
}
mActivityToken = newActivity;
@@ -1643,17 +1744,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private void setClientLocked(@NonNull IBinder client) {
unlinkClientVultureLocked();
mClient = IAutoFillManagerClient.Stub.asInterface(client);
- mClientVulture = () -> {
- synchronized (mLock) {
- Slog.d(TAG, "handling death of " + mActivityToken + " when saving="
- + mSessionFlags.mShowingSaveUi);
- if (mSessionFlags.mShowingSaveUi) {
- mUi.hideFillUi(this);
- } else {
- mUi.destroyAll(mPendingSaveUi, this, false);
- }
- }
- };
+ mClientVulture =
+ () -> {
+ synchronized (mLock) {
+ Slog.d(
+ TAG,
+ "handling death of "
+ + mActivityToken
+ + " when saving="
+ + mSessionFlags.mShowingSaveUi);
+ if (mSessionFlags.mShowingSaveUi) {
+ mUi.hideFillUi(this);
+ } else {
+ mUi.destroyAll(mPendingSaveUi, this, false);
+ }
+ }
+ };
try {
mClient.asBinder().linkToDeath(mClientVulture, 0);
} catch (RemoteException e) {
@@ -1676,8 +1782,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// FillServiceCallbacks
@Override
@SuppressWarnings("GuardedBy")
- public void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
- @NonNull String servicePackageName, int requestFlags) {
+ public void onFillRequestSuccess(
+ int requestId,
+ @Nullable FillResponse response,
+ @NonNull String servicePackageName,
+ int requestFlags) {
final AutofillId[] fieldClassificationIds;
final LogMaker requestLog;
@@ -1701,8 +1810,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
getDetectionPreferenceForLogging());
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#onFillRequestSuccess() rejected - session: "
+ + id
+ + " destroyed");
mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED);
mFillResponseEventLogger.logAndEndEvent();
return;
@@ -1713,8 +1825,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// saveUi gets closed, the session will be destroyed and AutofillManager will reset
// its state. Processing the fill request will result in a great chance of corrupt
// state in Autofill.
- Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: "
- + id + " is showing saveUi");
+ Slog.w(
+ TAG,
+ "Call to Session#onFillRequestSuccess() rejected - session: "
+ + id
+ + " is showing saveUi");
mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED);
mFillResponseEventLogger.logAndEndEvent();
return;
@@ -1760,22 +1875,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
-
final long disableDuration = response.getDisableDuration();
final boolean autofillDisabled = disableDuration > 0;
if (autofillDisabled) {
final int flags = response.getFlags();
final boolean disableActivityOnly =
(flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0;
- notifyDisableAutofillToClient(disableDuration,
- disableActivityOnly ? mComponentName : null);
+ notifyDisableAutofillToClient(
+ disableDuration, disableActivityOnly ? mComponentName : null);
if (disableActivityOnly) {
- mService.disableAutofillForActivity(mComponentName, disableDuration,
- id, mCompatMode);
+ mService.disableAutofillForActivity(
+ mComponentName, disableDuration, id, mCompatMode);
} else {
- mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration,
- id, mCompatMode);
+ mService.disableAutofillForApp(
+ mComponentName.getPackageName(), disableDuration, id, mCompatMode);
}
synchronized (mLock) {
@@ -1786,17 +1900,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (triggerAugmentedAutofillLocked(requestFlags) != null) {
mSessionFlags.mAugmentedAutofillOnly = true;
if (sDebug) {
- Slog.d(TAG, "Service disabled autofill for " + mComponentName
- + ", but session is kept for augmented autofill only");
+ Slog.d(
+ TAG,
+ "Service disabled autofill for "
+ + mComponentName
+ + ", but session is kept for augmented autofill only");
}
return;
}
}
if (sDebug) {
- final StringBuilder message = new StringBuilder("Service disabled autofill for ")
+ final StringBuilder message =
+ new StringBuilder("Service disabled autofill for ")
.append(mComponentName)
- .append(": flags=").append(flags)
+ .append(": flags=")
+ .append(flags)
.append(", duration=");
TimeUtils.formatDuration(disableDuration, message);
Slog.d(TAG, message.toString());
@@ -1816,8 +1935,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (requestLog != null) {
- requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
- response.getDatasets() == null ? 0 : response.getDatasets().size());
+ requestLog.addTaggedData(
+ MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
+ response.getDatasets() == null ? 0 : response.getDatasets().size());
if (fieldClassificationIds != null) {
requestLog.addTaggedData(
MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS,
@@ -1840,10 +1960,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
-
@GuardedBy("mLock")
- private void processResponseLockedForPcc(@NonNull FillResponse response,
- @Nullable Bundle newClientState, int flags) {
+ private void processResponseLockedForPcc(
+ @NonNull FillResponse response, @Nullable Bundle newClientState, int flags) {
if (DBG) {
Slog.d(TAG, "DBG: Initial response: " + response);
}
@@ -1851,9 +1970,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
response = getEffectiveFillResponse(response);
if (isEmptyResponse(response)) {
// Treat it as a null response.
- processNullResponseLocked(
- response != null ? response.getRequestId() : 0,
- flags);
+ processNullResponseLocked(response != null ? response.getRequestId() : 0, flags);
return;
}
if (DBG) {
@@ -1870,9 +1987,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return ((response.getDatasets() == null || response.getDatasets().isEmpty())
&& response.getAuthentication() == null
&& (saveInfo == null
- || (ArrayUtils.isEmpty(saveInfo.getOptionalIds())
- && ArrayUtils.isEmpty(saveInfo.getRequiredIds())
- && ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) == 0)))
+ || (ArrayUtils.isEmpty(saveInfo.getOptionalIds())
+ && ArrayUtils.isEmpty(saveInfo.getRequiredIds())
+ && ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) == 0)))
&& (ArrayUtils.isEmpty(response.getFieldClassificationIds())));
}
}
@@ -1884,10 +2001,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
computeDatasetsForProviderAndUpdateContainer(response, autofillProviderContainer);
if (DBG) {
- Slog.d(TAG, "DBG: computeDatasetsForProviderAndUpdateContainer: "
- + autofillProviderContainer);
+ Slog.d(
+ TAG,
+ "DBG: computeDatasetsForProviderAndUpdateContainer: "
+ + autofillProviderContainer);
}
- if (!mService.isPccClassificationEnabled()) {
+ if (!mService.isPccClassificationEnabled()) {
if (sVerbose) {
Slog.v(TAG, "PCC classification is disabled");
}
@@ -1897,10 +2016,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (mClassificationState.mState != ClassificationState.STATE_RESPONSE
|| mClassificationState.mLastFieldClassificationResponse == null) {
if (sVerbose) {
- Slog.v(TAG, "PCC classification no last response:"
- + (mClassificationState.mLastFieldClassificationResponse == null)
- + " ,ineligible state="
- + (mClassificationState.mState != ClassificationState.STATE_RESPONSE));
+ Slog.v(
+ TAG,
+ "PCC classification no last response:"
+ + (mClassificationState.mLastFieldClassificationResponse
+ == null)
+ + " ,ineligible state="
+ + (mClassificationState.mState
+ != ClassificationState.STATE_RESPONSE));
}
return createShallowCopy(response, autofillProviderContainer);
}
@@ -1966,8 +2089,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
(int) (fillRequestReceivedRelativeTimestamp));
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#onSecondaryFillResponse() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#onSecondaryFillResponse() rejected - session: "
+ + id
+ + " destroyed");
mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED);
mFillResponseEventLogger.logAndEndEvent();
return;
@@ -1981,7 +2107,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mSecondaryResponses = new SparseArray<>(2);
}
mSecondaryResponses.put(fillResponse.getRequestId(), fillResponse);
- setViewStatesLocked(fillResponse, ViewState.STATE_FILLABLE, /* clearResponse= */ false,
+ setViewStatesLocked(
+ fillResponse,
+ ViewState.STATE_FILLABLE,
+ /* clearResponse= */ false,
/* isPrimary= */ false);
// Updates the UI, if necessary.
@@ -1997,16 +2126,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private FillResponse createShallowCopy(
FillResponse response, DatasetComputationContainer container) {
return FillResponse.shallowCopy(
- response,
- new ArrayList<>(container.mDatasets),
- getEligibleSaveInfo(response));
+ response, new ArrayList<>(container.mDatasets), getEligibleSaveInfo(response));
}
private SaveInfo getEligibleSaveInfo(FillResponse response) {
SaveInfo saveInfo = response.getSaveInfo();
- if (saveInfo == null || (!ArrayUtils.isEmpty(saveInfo.getOptionalIds())
- || !ArrayUtils.isEmpty(saveInfo.getRequiredIds())
- || (saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0)) {
+ if (saveInfo == null
+ || (!ArrayUtils.isEmpty(saveInfo.getOptionalIds())
+ || !ArrayUtils.isEmpty(saveInfo.getRequiredIds())
+ || (saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0)) {
return saveInfo;
}
synchronized (mLock) {
@@ -2019,12 +2147,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
ArraySet<AutofillId> ids = new ArraySet<>();
int saveType = saveInfo.getType();
if (saveType == SaveInfo.SAVE_DATA_TYPE_GENERIC) {
- for (Set<AutofillId> autofillIds: hintsToAutofillIdMap.values()) {
+ for (Set<AutofillId> autofillIds : hintsToAutofillIdMap.values()) {
ids.addAll(autofillIds);
}
} else {
Set<String> hints = HintsHelper.getHintsForSaveType(saveType);
- for (Map.Entry<String, Set<AutofillId>> entry: hintsToAutofillIdMap.entrySet()) {
+ for (Map.Entry<String, Set<AutofillId>> entry : hintsToAutofillIdMap.entrySet()) {
String hint = entry.getKey();
if (hints.contains(hint)) {
ids.addAll(entry.getValue());
@@ -2039,9 +2167,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- /**
- * A private class to hold & compute datasets to be shown
- */
+ /** A private class to hold & compute datasets to be shown */
private static class DatasetComputationContainer {
// List of all autofill ids that have a corresponding datasets
Set<AutofillId> mAutofillIds = new LinkedHashSet<>();
@@ -2103,9 +2229,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
- * Computes datasets that are eligible to be shown based on provider detections.
- * Datasets are populated in the provided container for them to be later merged with the
- * PCC eligible datasets based on preference strategy.
+ * Computes datasets that are eligible to be shown based on provider detections. Datasets are
+ * populated in the provided container for them to be later merged with the PCC eligible
+ * datasets based on preference strategy.
+ *
* @param response
* @param container
*/
@@ -2210,9 +2337,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
- * Computes datasets that are eligible to be shown based on PCC detections.
- * Datasets are populated in the provided container for them to be later merged with the
- * provider eligible datasets based on preference strategy.
+ * Computes datasets that are eligible to be shown based on PCC detections. Datasets are
+ * populated in the provided container for them to be later merged with the provider eligible
+ * datasets based on preference strategy.
+ *
* @param response
* @param container
*/
@@ -2267,14 +2395,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// For that, there has to be a datatype detected by PCC, and the dataset
// for that datatype provided by the provider.
AutofillId autofillId = dataset.getFieldIds().get(j);
- if (!mClassificationState.mClassificationCombinedHintsMap
- .containsKey(autofillId)) {
+ if (!mClassificationState.mClassificationCombinedHintsMap.containsKey(
+ autofillId)) {
additionalEligibleAutofillIds.add(autofillId);
additionalDatasetAutofillIds.add(autofillId);
// For each of the field, copy over values.
- copyFieldsFromDataset(dataset, j, autofillId, fieldIds, fieldValues,
- fieldPresentations, fieldDialogPresentations,
- fieldInlinePresentations, fieldInlineTooltipPresentations,
+ copyFieldsFromDataset(
+ dataset,
+ j,
+ autofillId,
+ fieldIds,
+ fieldValues,
+ fieldPresentations,
+ fieldDialogPresentations,
+ fieldInlinePresentations,
+ fieldInlineTooltipPresentations,
fieldFilters);
}
continue;
@@ -2292,9 +2427,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
eligibleAutofillIds.add(autofillId);
datasetAutofillIds.add(autofillId);
// For each of the field, copy over values.
- copyFieldsFromDataset(dataset, j, autofillId, fieldIds, fieldValues,
- fieldPresentations, fieldDialogPresentations,
- fieldInlinePresentations, fieldInlineTooltipPresentations,
+ copyFieldsFromDataset(
+ dataset,
+ j,
+ autofillId,
+ fieldIds,
+ fieldValues,
+ fieldPresentations,
+ fieldDialogPresentations,
+ fieldInlinePresentations,
+ fieldInlineTooltipPresentations,
fieldFilters);
}
}
@@ -2364,8 +2506,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
fieldPresentations.add(dataset.getFieldPresentation(index));
fieldDialogPresentations.add(dataset.getFieldDialogPresentation(index));
fieldInlinePresentations.add(dataset.getFieldInlinePresentation(index));
- fieldInlineTooltipPresentations.add(
- dataset.getFieldInlineTooltipPresentation(index));
+ fieldInlineTooltipPresentations.add(dataset.getFieldInlineTooltipPresentation(index));
fieldFilters.add(dataset.getFilter(index));
}
@@ -2395,16 +2536,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
unregisterDelayedFillBroadcastLocked();
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId
- + ") rejected - session: " + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#onFillRequestFailureOrTimeout(req="
+ + requestId
+ + ") rejected - session: "
+ + id
+ + " destroyed");
mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED);
mFillResponseEventLogger.logAndEndEvent();
return;
}
if (sDebug) {
- Slog.d(TAG, "finishing session due to service "
- + (timedOut ? "timeout" : "failure"));
+ Slog.d(
+ TAG,
+ "finishing session due to service " + (timedOut ? "timeout" : "failure"));
}
mService.resetLastResponse();
mLastFillDialogTriggerIds = null;
@@ -2418,12 +2565,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final int targetSdk = mService.getTargedSdkLocked();
if (targetSdk >= Build.VERSION_CODES.Q) {
showMessage = false;
- Slog.w(TAG, "onFillRequestFailureOrTimeout(): not showing '" + message
- + "' because service's targetting API " + targetSdk);
+ Slog.w(
+ TAG,
+ "onFillRequestFailureOrTimeout(): not showing '"
+ + message
+ + "' because service's targeting API "
+ + targetSdk);
}
if (message != null) {
- requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_TEXT_LEN,
- message.length());
+ requestLog.addTaggedData(
+ MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, message.length());
}
}
@@ -2445,8 +2596,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis();
mFillResponseEventLogger.logAndEndEvent();
}
- notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED,
- /* autofillableIds= */ null);
+ notifyUnavailableToClient(
+ AutofillManager.STATE_UNKNOWN_FAILED, /* autofillableIds= */ null);
if (showMessage) {
getUiForShowing().showError(message, this);
}
@@ -2455,8 +2606,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// FillServiceCallbacks
@Override
- public void onSaveRequestSuccess(@NonNull String servicePackageName,
- @Nullable IntentSender intentSender) {
+ public void onSaveRequestSuccess(
+ @NonNull String servicePackageName, @Nullable IntentSender intentSender) {
synchronized (mLock) {
mSessionFlags.mShowingSaveUi = false;
// Log onSaveRequest result.
@@ -2464,16 +2615,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mSaveEventLogger.maybeSetLatencySaveFinishMillis();
mSaveEventLogger.logAndEndEvent();
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#onSaveRequestSuccess() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#onSaveRequestSuccess() rejected - session: "
+ + id
+ + " destroyed");
return;
}
}
- LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName)
- .setType(intentSender == null ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_OPEN);
+ LogMaker log =
+ newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName)
+ .setType(
+ intentSender == null
+ ? MetricsEvent.TYPE_SUCCESS
+ : MetricsEvent.TYPE_OPEN);
mMetricsLogger.write(log);
-
if (intentSender != null) {
if (sDebug) Slog.d(TAG, "Starting intent sender on save()");
startIntentSenderAndFinishSession(intentSender);
@@ -2485,8 +2642,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// FillServiceCallbacks
@Override
- public void onSaveRequestFailure(@Nullable CharSequence message,
- @NonNull String servicePackageName) {
+ public void onSaveRequestFailure(
+ @Nullable CharSequence message, @NonNull String servicePackageName) {
boolean showMessage = !TextUtils.isEmpty(message);
synchronized (mLock) {
@@ -2495,28 +2652,34 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mSaveEventLogger.maybeSetLatencySaveFinishMillis();
mSaveEventLogger.logAndEndEvent();
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#onSaveRequestFailure() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#onSaveRequestFailure() rejected - session: "
+ + id
+ + " destroyed");
return;
}
if (showMessage) {
final int targetSdk = mService.getTargedSdkLocked();
if (targetSdk >= Build.VERSION_CODES.Q) {
showMessage = false;
- Slog.w(TAG, "onSaveRequestFailure(): not showing '" + message
- + "' because service's targetting API " + targetSdk);
+ Slog.w(
+ TAG,
+ "onSaveRequestFailure(): not showing '"
+ + message
+ + "' because service's targeting API "
+ + targetSdk);
}
}
}
final LogMaker log =
newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName)
- .setType(MetricsEvent.TYPE_FAILURE);
+ .setType(MetricsEvent.TYPE_FAILURE);
if (message != null) {
log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, message.length());
}
mMetricsLogger.write(log);
-
if (showMessage) {
getUiForShowing().showError(message, this);
}
@@ -2525,8 +2688,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// FillServiceCallbacks
@Override
- public void onConvertCredentialRequestSuccess(@NonNull ConvertCredentialResponse
- convertCredentialResponse) {
+ public void onConvertCredentialRequestSuccess(
+ @NonNull ConvertCredentialResponse convertCredentialResponse) {
Dataset dataset = convertCredentialResponse.getDataset();
Bundle clientState = convertCredentialResponse.getClientState();
if (dataset != null) {
@@ -2534,15 +2697,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (clientState != null) {
requestId = clientState.getInt(EXTRA_AUTOFILL_REQUEST_ID);
} else {
- Slog.e(TAG, "onConvertCredentialRequestSuccess(): client state is null, this "
- + "would cause loss in logging.");
+ Slog.e(
+ TAG,
+ "onConvertCredentialRequestSuccess(): client state is null, this "
+ + "would cause loss in logging.");
}
// TODO: Add autofill related logging; consider whether to log the index
- fill(requestId, /* datasetIndex=*/ -1, dataset, UI_TYPE_CREDMAN_BOTTOM_SHEET);
+ fill(requestId, /* datasetIndex= */ -1, dataset, UI_TYPE_CREDMAN_BOTTOM_SHEET);
} else {
// TODO: Add logging to log this error case
- Slog.e(TAG, "onConvertCredentialRequestSuccess(): dataset inside response is "
- + "null");
+ Slog.e(
+ TAG,
+ "onConvertCredentialRequestSuccess(): dataset inside response is " + "null");
}
}
@@ -2550,11 +2716,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* Gets the {@link FillContext} for a request.
*
* @param requestId The id of the request
- *
* @return The context or {@code null} if there is no context
*/
@GuardedBy("mLock")
- @Nullable private FillContext getFillContextByRequestIdLocked(int requestId) {
+ @Nullable
+ private FillContext getFillContextByRequestIdLocked(int requestId) {
if (mContexts == null) {
return null;
}
@@ -2582,19 +2748,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// AutoFillUiCallback
@Override
- public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras,
- int uiType) {
+ public void authenticate(
+ int requestId, int datasetIndex, IntentSender intent, Bundle extras, int uiType) {
if (sDebug) {
- Slog.d(TAG, "authenticate(): requestId=" + requestId + "; datasetIdx=" + datasetIndex
- + "; intentSender=" + intent);
+ Slog.d(
+ TAG,
+ "authenticate(): requestId="
+ + requestId
+ + "; datasetIdx="
+ + datasetIndex
+ + "; intentSender="
+ + intent);
}
final Intent fillInIntent;
synchronized (mLock) {
mPresentationStatsEventLogger.maybeSetAuthenticationType(
- AUTHENTICATION_TYPE_FULL_AUTHENTICATION);
+ AUTHENTICATION_TYPE_FULL_AUTHENTICATION);
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#authenticate() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#authenticate() rejected - session: " + id + " destroyed");
return;
}
fillInIntent = createAuthFillInIntentLocked(requestId, extras);
@@ -2607,10 +2780,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mService.setAuthenticationSelected(id, mClientState, uiType);
final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex);
- mHandler.sendMessage(obtainMessage(
- Session::startAuthentication,
- this, authenticationId, intent, fillInIntent,
- /* authenticateInline= */ uiType == UI_TYPE_INLINE));
+ mHandler.sendMessage(
+ obtainMessage(
+ Session::startAuthentication,
+ this,
+ authenticationId,
+ intent,
+ fillInIntent,
+ /* authenticateInline= */ uiType == UI_TYPE_INLINE));
}
// AutoFillUiCallback
@@ -2618,14 +2795,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
public void fill(int requestId, int datasetIndex, Dataset dataset, int uiType) {
synchronized (mLock) {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#fill() rejected - session: "
- + id + " destroyed");
+ Slog.w(TAG, "Call to Session#fill() rejected - session: " + id + " destroyed");
return;
}
}
- mHandler.sendMessage(obtainMessage(
- Session::autoFill,
- this, requestId, datasetIndex, dataset, true, uiType));
+ mHandler.sendMessage(
+ obtainMessage(
+ Session::autoFill, this, requestId, datasetIndex, dataset, true, uiType));
}
// AutoFillUiCallback
@@ -2633,15 +2809,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
public void save() {
synchronized (mLock) {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#save() rejected - session: "
- + id + " destroyed");
+ Slog.w(TAG, "Call to Session#save() rejected - session: " + id + " destroyed");
return;
}
mSaveEventLogger.maybeSetLatencySaveRequestMillis();
}
- mHandler.sendMessage(obtainMessage(
- AutofillManagerServiceImpl::handleSessionSave,
- mService, this));
+ mHandler.sendMessage(
+ obtainMessage(AutofillManagerServiceImpl::handleSessionSave, mService, this));
}
// AutoFillUiCallback
@@ -2650,13 +2824,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
synchronized (mLock) {
mSessionFlags.mShowingSaveUi = false;
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#cancelSave() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#cancelSave() rejected - session: " + id + " destroyed");
return;
}
}
- mHandler.sendMessage(obtainMessage(
- Session::removeFromService, this));
+ mHandler.sendMessage(obtainMessage(Session::removeFromService, this));
}
// AutofillUiCallback
@@ -2692,26 +2866,34 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// AutoFillUiCallback
@Override
- public void requestShowFillUi(AutofillId id, int width, int height,
- IAutofillWindowPresenter presenter) {
+ public void requestShowFillUi(
+ AutofillId id, int width, int height, IAutofillWindowPresenter presenter) {
synchronized (mLock) {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#requestShowFillUi() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#requestShowFillUi() rejected - session: "
+ + id
+ + " destroyed");
return;
}
if (id.equals(mCurrentViewId)) {
try {
final ViewState view = mViewStates.get(id);
- mClient.requestShowFillUi(this.id, id, width, height, view.getVirtualBounds(),
- presenter);
+ mClient.requestShowFillUi(
+ this.id, id, width, height, view.getVirtualBounds(), presenter);
} catch (RemoteException e) {
Slog.e(TAG, "Error requesting to show fill UI", e);
}
} else {
if (sDebug) {
- Slog.d(TAG, "Do not show full UI on " + id + " as it is not the current view ("
- + mCurrentViewId + ") anymore");
+ Slog.d(
+ TAG,
+ "Do not show full UI on "
+ + id
+ + " as it is not the current view ("
+ + mCurrentViewId
+ + ") anymore");
}
}
}
@@ -2722,8 +2904,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
public void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent) {
synchronized (mLock) {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#dispatchUnhandledKey() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#dispatchUnhandledKey() rejected - session: "
+ + id
+ + " destroyed");
return;
}
if (id.equals(mCurrentViewId)) {
@@ -2733,8 +2918,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.e(TAG, "Error requesting to dispatch unhandled key", e);
}
} else {
- Slog.w(TAG, "Do not dispatch unhandled key on " + id
- + " as it is not the current view (" + mCurrentViewId + ") anymore");
+ Slog.w(
+ TAG,
+ "Do not dispatch unhandled key on "
+ + id
+ + " as it is not the current view ("
+ + mCurrentViewId
+ + ") anymore");
}
}
}
@@ -2790,17 +2980,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
public void startIntentSender(IntentSender intentSender, Intent intent) {
synchronized (mLock) {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#startIntentSender() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#startIntentSender() rejected - session: "
+ + id
+ + " destroyed");
return;
}
if (intent == null) {
removeFromServiceLocked();
}
}
- mHandler.sendMessage(obtainMessage(
- Session::doStartIntentSender,
- this, intentSender, intent));
+ mHandler.sendMessage(
+ obtainMessage(Session::doStartIntentSender, this, intentSender, intent));
}
// AutoFillUiCallback
@@ -2865,13 +3057,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
void setAuthenticationResultLocked(Bundle data, int authenticationId) {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#setAuthenticationResultLocked() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#setAuthenticationResultLocked() rejected - session: "
+ + id
+ + " destroyed");
return;
}
if (sDebug) {
- Slog.d(TAG, "setAuthenticationResultLocked(): id= " + authenticationId
- + ", data=" + data);
+ Slog.d(
+ TAG,
+ "setAuthenticationResultLocked(): id= " + authenticationId + ", data=" + data);
}
final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId);
if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) {
@@ -2890,9 +3086,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
removeFromService();
return;
}
- final FillResponse authenticatedResponse = mRequestId.isSecondaryProvider(requestId)
- ? mSecondaryResponses.get(requestId)
- : mResponses.get(requestId);
+ final FillResponse authenticatedResponse =
+ mRequestId.isSecondaryProvider(requestId)
+ ? mSecondaryResponses.get(requestId)
+ : mResponses.get(requestId);
if (authenticatedResponse == null || data == null) {
Slog.w(TAG, "no authenticated response");
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
@@ -2902,8 +3099,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
- final int datasetIdx = AutofillManager.getDatasetIdFromAuthenticationId(
- authenticationId);
+ final int datasetIdx = AutofillManager.getDatasetIdFromAuthenticationId(authenticationId);
Dataset dataset = null;
// Authenticated a dataset - reset view state regardless if we got a response or a dataset
if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
@@ -2922,33 +3118,45 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mSessionFlags.mExpiredResponse = false;
final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
- final GetCredentialException exception = data.getSerializable(
- CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
- GetCredentialException.class);
+ final GetCredentialException exception =
+ data.getSerializable(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
+ GetCredentialException.class);
final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE);
if (sDebug) {
- Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result
- + ", clientState=" + newClientState + ", authenticationId=" + authenticationId);
- }
- if (Flags.autofillCredmanDevIntegration() && exception != null
+ Slog.d(
+ TAG,
+ "setAuthenticationResultLocked(): result="
+ + result
+ + ", clientState="
+ + newClientState
+ + ", authenticationId="
+ + authenticationId);
+ }
+ if (Flags.autofillCredmanDevIntegration()
+ && exception != null
&& !exception.getType().equals(GetCredentialException.TYPE_USER_CANCELED)) {
if (dataset != null && dataset.getFieldIds().size() == 1) {
if (sDebug) {
- Slog.d(TAG, "setAuthenticationResultLocked(): result returns with"
- + "Credential Manager Exception");
+ Slog.d(
+ TAG,
+ "setAuthenticationResultLocked(): result returns with"
+ + "Credential Manager Exception");
}
AutofillId autofillId = dataset.getFieldIds().get(0);
- sendCredentialManagerResponseToApp(/*response=*/ null,
- (GetCredentialException) exception, autofillId);
+ sendCredentialManagerResponseToApp(
+ /* response= */ null, (GetCredentialException) exception, autofillId);
}
return;
}
if (result instanceof FillResponse) {
if (sDebug) {
- Slog.d(TAG, "setAuthenticationResultLocked(): received FillResponse from"
- + " authentication flow");
+ Slog.d(
+ TAG,
+ "setAuthenticationResultLocked(): received FillResponse from"
+ + " authentication flow");
}
logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED);
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
@@ -2963,33 +3171,40 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (dataset != null && dataset.getFieldIds().size() == 1) {
AutofillId autofillId = dataset.getFieldIds().get(0);
if (sDebug) {
- Slog.d(TAG, "Received GetCredentialResponse from authentication flow,"
- + "for autofillId: " + autofillId);
+ Slog.d(
+ TAG,
+ "Received GetCredentialResponse from authentication flow,"
+ + "for autofillId: "
+ + autofillId);
}
- sendCredentialManagerResponseToApp(response,
- /*exception=*/ null, autofillId);
+ sendCredentialManagerResponseToApp(response, /* exception= */ null, autofillId);
}
} else if (Flags.autofillCredmanIntegration()) {
- Dataset datasetFromCredentialResponse = getDatasetFromCredentialResponse(
- (GetCredentialResponse) result);
+ Dataset datasetFromCredentialResponse =
+ getDatasetFromCredentialResponse((GetCredentialResponse) result);
if (datasetFromCredentialResponse != null) {
- autoFill(requestId, datasetIdx, datasetFromCredentialResponse,
- false, UI_TYPE_UNKNOWN);
+ autoFill(
+ requestId,
+ datasetIdx,
+ datasetFromCredentialResponse,
+ false,
+ UI_TYPE_UNKNOWN);
}
}
} else if (result instanceof Dataset) {
if (sDebug) {
- Slog.d(TAG, "setAuthenticationResultLocked(): received Dataset from"
- + " authentication flow");
+ Slog.d(
+ TAG,
+ "setAuthenticationResultLocked(): received Dataset from"
+ + " authentication flow");
}
if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
- logAuthenticationStatusLocked(requestId,
- MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
+ logAuthenticationStatusLocked(
+ requestId, MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
AUTHENTICATION_RESULT_SUCCESS);
if (newClientState != null) {
- if (sDebug)
- Slog.d(TAG, "Updating client state from auth dataset");
+ if (sDebug) Slog.d(TAG, "Updating client state from auth dataset");
mClientState = newClientState;
}
Dataset datasetFromResult = getEffectiveDatasetForAuthentication((Dataset) result);
@@ -2999,10 +3214,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
autoFill(requestId, datasetIdx, datasetFromResult, false, UI_TYPE_UNKNOWN);
} else {
- Slog.w(TAG, "invalid index (" + datasetIdx + ") for authentication id "
- + authenticationId);
- logAuthenticationStatusLocked(requestId,
- MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION);
+ Slog.w(
+ TAG,
+ "invalid index ("
+ + datasetIdx
+ + ") for authentication id "
+ + authenticationId);
+ logAuthenticationStatusLocked(
+ requestId, MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION);
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
AUTHENTICATION_RESULT_FAILURE);
}
@@ -3010,8 +3229,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (result != null) {
Slog.w(TAG, "service returned invalid auth type: " + result);
}
- logAuthenticationStatusLocked(requestId,
- MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION);
+ logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION);
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
AUTHENTICATION_RESULT_FAILURE);
processNullResponseLocked(requestId, 0);
@@ -3036,8 +3254,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.d(TAG, "DBG: authenticated effective response: " + response);
}
if (response == null || response.getDatasets().size() == 0) {
- Log.wtf(TAG, "No datasets in fill response on authentication. response = "
- + (response == null ? "null" : response.toString()));
+ Log.wtf(
+ TAG,
+ "No datasets in fill response on authentication. response = "
+ + (response == null ? "null" : response.toString()));
return authenticatedDataset;
}
List<Dataset> datasets = response.getDatasets();
@@ -3047,8 +3267,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
for (Dataset dataset : datasets) {
if (!dataset.getFieldIds().isEmpty()) {
for (int i = 0; i < dataset.getFieldIds().size(); i++) {
- builder.setField(dataset.getFieldIds().get(i),
- new Field.Builder().setValue(dataset.getFieldValues().get(i))
+ builder.setField(
+ dataset.getFieldIds().get(i),
+ new Field.Builder()
+ .setValue(dataset.getFieldValues().get(i))
.build());
}
}
@@ -3063,12 +3285,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
- * Returns whether the dataset returned from the authentication result is ephemeral or not.
- * See {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more
- * information.
+ * Returns whether the dataset returned from the authentication result is ephemeral or not. See
+ * {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more information.
*/
- private static boolean isAuthResultDatasetEphemeral(@Nullable Dataset oldDataset,
- @NonNull Bundle authResultData) {
+ private static boolean isAuthResultDatasetEphemeral(
+ @Nullable Dataset oldDataset, @NonNull Bundle authResultData) {
if (authResultData.containsKey(
AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) {
return authResultData.getBoolean(
@@ -3079,11 +3300,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/**
* A dataset can potentially have multiple fields, and it's possible that some of the fields'
- * has inline presentation and some don't. It's also possible that some of the fields'
- * inline presentation is pinned and some isn't. So the concept of whether a dataset is
- * pinned or not is ill-defined. Here we say a dataset is pinned if any of the field has a
- * pinned inline presentation in the dataset. It's not ideal but hopefully it is sufficient
- * for most of the cases.
+ * has inline presentation and some don't. It's also possible that some of the fields' inline
+ * presentation is pinned and some isn't. So the concept of whether a dataset is pinned or not
+ * is ill-defined. Here we say a dataset is pinned if any of the field has a pinned inline
+ * presentation in the dataset. It's not ideal but hopefully it is sufficient for most of the
+ * cases.
*/
private static boolean isPinnedDataset(@Nullable Dataset dataset) {
if (dataset != null && dataset.getFieldIds() != null) {
@@ -3100,16 +3321,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
void setAuthenticationResultForAugmentedAutofillLocked(Bundle data, int authId) {
- final Dataset dataset = (data == null) ? null :
- data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT, android.service.autofill.Dataset.class);
+ final Dataset dataset =
+ (data == null)
+ ? null
+ : data.getParcelable(
+ AutofillManager.EXTRA_AUTHENTICATION_RESULT,
+ android.service.autofill.Dataset.class);
if (sDebug) {
- Slog.d(TAG, "Auth result for augmented autofill: sessionId=" + id
- + ", authId=" + authId + ", dataset=" + dataset);
- }
- final AutofillId fieldId = (dataset != null && dataset.getFieldIds().size() == 1)
- ? dataset.getFieldIds().get(0) : null;
- final AutofillValue value = (dataset != null && dataset.getFieldValues().size() == 1)
- ? dataset.getFieldValues().get(0) : null;
+ Slog.d(
+ TAG,
+ "Auth result for augmented autofill: sessionId="
+ + id
+ + ", authId="
+ + authId
+ + ", dataset="
+ + dataset);
+ }
+ final AutofillId fieldId =
+ (dataset != null && dataset.getFieldIds().size() == 1)
+ ? dataset.getFieldIds().get(0)
+ : null;
+ final AutofillValue value =
+ (dataset != null && dataset.getFieldValues().size() == 1)
+ ? dataset.getFieldValues().get(0)
+ : null;
final ClipData content = (dataset != null) ? dataset.getFieldContent() : null;
if (fieldId == null || (value == null && content == null)) {
if (sDebug) {
@@ -3154,8 +3389,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Fill the value into the field.
if (sDebug) {
- Slog.d(TAG, "Filling after auth: fieldId=" + fieldId + ", value=" + value
- + ", content=" + content);
+ Slog.d(
+ TAG,
+ "Filling after auth: fieldId="
+ + fieldId
+ + ", value="
+ + value
+ + ", content="
+ + content);
}
try {
if (content != null) {
@@ -3164,8 +3405,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mClient.autofill(id, dataset.getFieldIds(), dataset.getFieldValues(), true);
}
} catch (RemoteException e) {
- Slog.w(TAG, "Error filling after auth: fieldId=" + fieldId + ", value=" + value
- + ", content=" + content, e);
+ Slog.w(
+ TAG,
+ "Error filling after auth: fieldId="
+ + fieldId
+ + ", value="
+ + value
+ + ", content="
+ + content,
+ e);
}
// Clear the suggestions since the user already accepted one of them.
@@ -3175,8 +3423,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
void setHasCallbackLocked(boolean hasIt) {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#setHasCallbackLocked() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#setHasCallbackLocked() rejected - session: "
+ + id
+ + " destroyed");
return;
}
mHasCallback = hasIt;
@@ -3185,9 +3436,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
@Nullable
private FillResponse getLastResponseLocked(@Nullable String logPrefixFmt) {
- final String logPrefix = sDebug && logPrefixFmt != null
- ? String.format(logPrefixFmt, this.id)
- : null;
+ final String logPrefix =
+ sDebug && logPrefixFmt != null ? String.format(logPrefixFmt, this.id) : null;
if (mContexts == null) {
if (logPrefix != null) Slog.d(TAG, logPrefix + ": no contexts");
return null;
@@ -3204,16 +3454,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final int lastResponseIdx = getLastResponseIndexLocked();
if (lastResponseIdx < 0) {
if (logPrefix != null) {
- Slog.w(TAG, logPrefix + ": did not get last response. mResponses=" + mResponses
- + ", mViewStates=" + mViewStates);
+ Slog.w(
+ TAG,
+ logPrefix
+ + ": did not get last response. mResponses="
+ + mResponses
+ + ", mViewStates="
+ + mViewStates);
}
return null;
}
final FillResponse response = mResponses.valueAt(lastResponseIdx);
if (sVerbose && logPrefix != null) {
- Slog.v(TAG, logPrefix + ": mResponses=" + mResponses + ", mContexts=" + mContexts
- + ", mViewStates=" + mViewStates);
+ Slog.v(
+ TAG,
+ logPrefix
+ + ": mResponses="
+ + mResponses
+ + ", mContexts="
+ + mContexts
+ + ", mViewStates="
+ + mViewStates);
}
return response;
}
@@ -3232,9 +3494,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
- * Get statistic information of save info in current session. Specifically
- * 1. how many save info the current session has.
- * 2. How many distinct save data types current session has.
+ * Get statistic information of save info in current session. Specifically 1. how many save info
+ * the current session has. 2. How many distinct save data types current session has.
*
* @return SaveInfoStats returns the above two number in a SaveInfoStats object
*/
@@ -3255,12 +3516,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
public void logContextCommitted() {
if (sVerbose) {
- Slog.v(TAG, "logContextCommitted (" + id + "): commit_reason:" + COMMIT_REASON_UNKNOWN
- + " no_save_reason:" + Event.NO_SAVE_UI_REASON_NONE);
- }
- mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
- Event.NO_SAVE_UI_REASON_NONE,
- COMMIT_REASON_UNKNOWN));
+ Slog.v(
+ TAG,
+ "logContextCommitted ("
+ + id
+ + "): commit_reason:"
+ + COMMIT_REASON_UNKNOWN
+ + " no_save_reason:"
+ + Event.NO_SAVE_UI_REASON_NONE);
+ }
+ mHandler.sendMessage(
+ obtainMessage(
+ Session::handleLogContextCommitted,
+ this,
+ Event.NO_SAVE_UI_REASON_NONE,
+ COMMIT_REASON_UNKNOWN));
synchronized (mLock) {
logAllEventsLocked(COMMIT_REASON_UNKNOWN);
}
@@ -3274,16 +3544,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* @param saveDialogNotShowReason The reason why a save dialog was not shown.
* @param commitReason The reason why context is committed.
*/
-
@GuardedBy("mLock")
- public void logContextCommittedLocked(@NoSaveReason int saveDialogNotShowReason,
- @AutofillCommitReason int commitReason) {
+ public void logContextCommittedLocked(
+ @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) {
if (sVerbose) {
- Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason
- + " no_save_reason:" + saveDialogNotShowReason);
- }
- mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
- saveDialogNotShowReason, commitReason));
+ Slog.v(
+ TAG,
+ "logContextCommittedLocked ("
+ + id
+ + "): commit_reason:"
+ + commitReason
+ + " no_save_reason:"
+ + saveDialogNotShowReason);
+ }
+ mHandler.sendMessage(
+ obtainMessage(
+ Session::handleLogContextCommitted,
+ this,
+ saveDialogNotShowReason,
+ commitReason));
mSessionCommittedEventLogger.maybeSetCommitReason(commitReason);
mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
@@ -3295,8 +3574,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE);
}
- private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason,
- @AutofillCommitReason int commitReason) {
+ private void handleLogContextCommitted(
+ @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) {
final FillResponse lastResponse;
synchronized (mLock) {
lastResponse = getLastResponseLocked("logContextCommited(%s)");
@@ -3326,31 +3605,42 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Sets field classification scores
if (userData != null && fcStrategy != null) {
- logFieldClassificationScore(fcStrategy, userData, saveDialogNotShowReason,
- commitReason);
+ logFieldClassificationScore(
+ fcStrategy, userData, saveDialogNotShowReason, commitReason);
} else {
logContextCommitted(null, null, saveDialogNotShowReason, commitReason);
}
}
- private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds,
+ private void logContextCommitted(
+ @Nullable ArrayList<AutofillId> detectedFieldIds,
@Nullable ArrayList<FieldClassification> detectedFieldClassifications,
@NoSaveReason int saveDialogNotShowReason,
@AutofillCommitReason int commitReason) {
synchronized (mLock) {
- logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications,
- saveDialogNotShowReason, commitReason);
+ logContextCommittedLocked(
+ detectedFieldIds,
+ detectedFieldClassifications,
+ saveDialogNotShowReason,
+ commitReason);
}
}
@GuardedBy("mLock")
- private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds,
+ private void logContextCommittedLocked(
+ @Nullable ArrayList<AutofillId> detectedFieldIds,
@Nullable ArrayList<FieldClassification> detectedFieldClassifications,
@NoSaveReason int saveDialogNotShowReason,
@AutofillCommitReason int commitReason) {
if (sVerbose) {
- Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason
- + " no_save_reason:" + saveDialogNotShowReason);
+ Slog.v(
+ TAG,
+ "logContextCommittedLocked ("
+ + id
+ + "): commit_reason:"
+ + commitReason
+ + " no_save_reason:"
+ + saveDialogNotShowReason);
}
final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)");
if (lastResponse == null) return;
@@ -3423,8 +3713,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final AutofillValue currentValue = viewState.getCurrentValue();
if (autofilledValue != null && autofilledValue.equals(currentValue)) {
if (sDebug) {
- Slog.d(TAG, "logContextCommitted(): ignoring changed " + viewState
- + " because it has same value that was autofilled");
+ Slog.d(
+ TAG,
+ "logContextCommitted(): ignoring changed "
+ + viewState
+ + " because it has same value that was autofilled");
}
continue;
}
@@ -3442,8 +3735,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final AutofillValue currentValue = viewState.getCurrentValue();
if (currentValue == null) {
if (sDebug) {
- Slog.d(TAG, "logContextCommitted(): skipping view without current "
- + "value ( " + viewState + ")");
+ Slog.d(
+ TAG,
+ "logContextCommitted(): skipping view without current "
+ + "value ( "
+ + viewState
+ + ")");
}
continue;
}
@@ -3455,7 +3752,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final List<Dataset> datasets = response.getDatasets();
if (datasets == null || datasets.isEmpty()) {
if (sVerbose) {
- Slog.v(TAG, "logContextCommitted() no datasets at " + j);
+ Slog.v(TAG, "logContextCommitted() no datasets at " + j);
}
} else {
for (int k = 0; k < datasets.size(); k++) {
@@ -3463,8 +3760,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final String datasetId = dataset.getId();
if (datasetId == null) {
if (sVerbose) {
- Slog.v(TAG, "logContextCommitted() skipping idless "
- + "dataset " + dataset);
+ Slog.v(
+ TAG,
+ "logContextCommitted() skipping idless "
+ + "dataset "
+ + dataset);
}
} else {
final ArrayList<AutofillValue> values =
@@ -3473,9 +3773,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final AutofillValue candidate = values.get(l);
if (currentValue.equals(candidate)) {
if (sDebug) {
- Slog.d(TAG, "field " + viewState.id + " was "
- + "manually filled with value set by "
- + "dataset " + datasetId);
+ Slog.d(
+ TAG,
+ "field "
+ + viewState.id
+ + " was manually filled with"
+ + " value set by dataset "
+ + datasetId);
}
if (manuallyFilledIds == null) {
manuallyFilledIds = new ArrayMap<>();
@@ -3524,10 +3828,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, ignoredDatasets,
- changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
- manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
- mComponentName, mCompatMode, saveDialogNotShowReason);
+ mService.logContextCommittedLocked(
+ id,
+ mClientState,
+ mSelectedDatasetIds,
+ ignoredDatasets,
+ changedFieldIds,
+ changedDatasetIds,
+ manuallyFilledFieldIds,
+ manuallyFilledDatasetIds,
+ detectedFieldIds,
+ detectedFieldClassifications,
+ mComponentName,
+ mCompatMode,
+ saveDialogNotShowReason);
mSessionCommittedEventLogger.maybeSetCommitReason(commitReason);
mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
mSaveEventLogger.maybeSetSaveUiNotShownReason(saveDialogNotShowReason);
@@ -3537,7 +3851,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* Adds the matches to {@code detectedFieldsIds} and {@code detectedFieldClassifications} for
* {@code fieldId} based on its {@code currentValue} and {@code userData}.
*/
- private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy,
+ private void logFieldClassificationScore(
+ @NonNull FieldClassificationStrategy fcStrategy,
@NonNull FieldClassificationUserData userData,
@NoSaveReason int saveDialogNotShowReason,
@AutofillCommitReason int commitReason) {
@@ -3555,16 +3870,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (userValues == null || categoryIds == null || userValues.length != categoryIds.length) {
final int valuesLength = userValues == null ? -1 : userValues.length;
final int idsLength = categoryIds == null ? -1 : categoryIds.length;
- Slog.w(TAG, "setScores(): user data mismatch: values.length = "
- + valuesLength + ", ids.length = " + idsLength);
+ Slog.w(
+ TAG,
+ "setScores(): user data mismatch: values.length = "
+ + valuesLength
+ + ", ids.length = "
+ + idsLength);
return;
}
final int maxFieldsSize = UserData.getMaxFieldClassificationIdsSize();
final ArrayList<AutofillId> detectedFieldIds = new ArrayList<>(maxFieldsSize);
- final ArrayList<FieldClassification> detectedFieldClassifications = new ArrayList<>(
- maxFieldsSize);
+ final ArrayList<FieldClassification> detectedFieldClassifications =
+ new ArrayList<>(maxFieldsSize);
final Collection<ViewState> viewStates;
synchronized (mLock) {
@@ -3583,33 +3902,49 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
// Then use the results, asynchronously
- final RemoteCallback callback = new RemoteCallback(
- new LogFieldClassificationScoreOnResultListener(
- this,
- saveDialogNotShowReason,
- commitReason,
- viewsSize,
- autofillIds,
- userValues,
- categoryIds,
- detectedFieldIds,
- detectedFieldClassifications));
-
- fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds,
- defaultAlgorithm, defaultArgs, algorithms, args);
- }
-
- void handleLogFieldClassificationScore(@Nullable Bundle result, int saveDialogNotShowReason,
- int commitReason, int viewsSize, AutofillId[] autofillIds, String[] userValues,
- String[] categoryIds, ArrayList<AutofillId> detectedFieldIds,
+ final RemoteCallback callback =
+ new RemoteCallback(
+ new LogFieldClassificationScoreOnResultListener(
+ this,
+ saveDialogNotShowReason,
+ commitReason,
+ viewsSize,
+ autofillIds,
+ userValues,
+ categoryIds,
+ detectedFieldIds,
+ detectedFieldClassifications));
+
+ fcStrategy.calculateScores(
+ callback,
+ currentValues,
+ userValues,
+ categoryIds,
+ defaultAlgorithm,
+ defaultArgs,
+ algorithms,
+ args);
+ }
+
+ void handleLogFieldClassificationScore(
+ @Nullable Bundle result,
+ int saveDialogNotShowReason,
+ int commitReason,
+ int viewsSize,
+ AutofillId[] autofillIds,
+ String[] userValues,
+ String[] categoryIds,
+ ArrayList<AutofillId> detectedFieldIds,
ArrayList<FieldClassification> detectedFieldClassifications) {
if (result == null) {
if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results");
logContextCommitted(null, null, saveDialogNotShowReason, commitReason);
return;
}
- final Scores scores = result.getParcelable(EXTRA_SCORES,
- android.service.autofill.AutofillFieldClassificationService.Scores.class);
+ final Scores scores =
+ result.getParcelable(
+ EXTRA_SCORES,
+ android.service.autofill.AutofillFieldClassificationService.Scores.class);
if (scores == null) {
Slog.w(TAG, "No field classification score on " + result);
return;
@@ -3633,14 +3968,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final Float currentScore = scoresByField.get(categoryId);
if (currentScore != null && currentScore > score) {
if (sVerbose) {
- Slog.v(TAG, "skipping score " + score
- + " because it's less than " + currentScore);
+ Slog.v(
+ TAG,
+ "skipping score "
+ + score
+ + " because it's less than "
+ + currentScore);
}
continue;
}
if (sVerbose) {
- Slog.v(TAG, "adding score " + score + " at index " + j + " and id "
- + autofillId);
+ Slog.v(
+ TAG,
+ "adding score "
+ + score
+ + " at index "
+ + j
+ + " and id "
+ + autofillId);
}
scoresByField.put(categoryId, score);
} else if (sVerbose) {
@@ -3666,13 +4011,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e);
return;
}
- logContextCommitted(detectedFieldIds, detectedFieldClassifications,
- saveDialogNotShowReason, commitReason);
+ logContextCommitted(
+ detectedFieldIds,
+ detectedFieldClassifications,
+ saveDialogNotShowReason,
+ commitReason);
}
/**
- * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN}
- * when necessary.
+ * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} when
+ * necessary.
*
* <p>Note: It is necessary to call logContextCommitted() first before calling this method.
*/
@@ -3689,11 +4037,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@NonNull
public SaveResult showSaveLocked() {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#showSaveLocked() rejected - session: " + id + " destroyed");
mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SESSION_DESTROYED);
mSaveEventLogger.logAndEndEvent();
- return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false,
+ return new SaveResult(
+ /* logSaveShown= */ false,
+ /* removeSession= */ false,
Event.NO_SAVE_UI_REASON_NONE);
}
mSessionState = STATE_FINISHED;
@@ -3705,12 +4056,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
if (mSessionFlags.mScreenHasCredmanField) {
if (sVerbose) {
- Slog.v(TAG, "Call to Session#showSaveLocked() rejected - "
- + "there is credman field in screen");
+ Slog.v(
+ TAG,
+ "Call to Session#showSaveLocked() rejected - "
+ + "there is credman field in screen");
}
mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SCREEN_HAS_CREDMAN_FIELD);
mSaveEventLogger.logAndEndEvent();
- return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+ return new SaveResult(
+ /* logSaveShown= */ false,
+ /* removeSession= */ true,
Event.NO_SAVE_UI_REASON_NONE);
}
@@ -3728,7 +4083,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sVerbose) Slog.v(TAG, "showSaveLocked(" + this.id + "): no saveInfo from service");
mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NO_SAVE_INFO);
mSaveEventLogger.logAndEndEvent();
- return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+ return new SaveResult(
+ /* logSaveShown= */ false,
+ /* removeSession= */ true,
Event.NO_SAVE_UI_REASON_NO_SAVE_INFO);
}
@@ -3737,7 +4094,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sDebug) Slog.v(TAG, "showSaveLocked(" + this.id + "): service asked to delay save");
mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG);
mSaveEventLogger.logAndEndEvent();
- return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false,
+ return new SaveResult(
+ /* logSaveShown= */ false,
+ /* removeSession= */ false,
Event.NO_SAVE_UI_REASON_WITH_DELAY_SAVE_FLAG);
}
@@ -3774,12 +4133,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Some apps clear the form before navigating to other activities.
// If current value is empty, consider fall back to last cached
// non-empty result first.
- final AutofillValue candidateSaveValue =
- viewState.getCandidateSaveValue();
+ final AutofillValue candidateSaveValue = viewState.getCandidateSaveValue();
if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) {
if (sVerbose) {
- Slog.v(TAG, "current value is empty, using cached last non-empty "
- + "value instead");
+ Slog.v(
+ TAG,
+ "current value is empty, using cached last non-empty "
+ + "value instead");
}
value = candidateSaveValue;
} else {
@@ -3788,8 +4148,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final AutofillValue initialValue = getValueFromContextsLocked(id);
if (initialValue != null) {
if (sDebug) {
- Slog.d(TAG, "Value of required field " + id + " didn't change; "
- + "using initial value (" + initialValue + ") instead");
+ Slog.d(
+ TAG,
+ "Value of required field "
+ + id
+ + " didn't change; "
+ + "using initial value ("
+ + initialValue
+ + ") instead");
}
value = initialValue;
} else {
@@ -3821,8 +4187,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final AutofillValue initialValue = getValueFromContextsLocked(id);
if (initialValue != null && initialValue.equals(value)) {
if (sDebug) {
- Slog.d(TAG, "id " + id + " is part of dataset but initial value "
- + "didn't change: " + value);
+ Slog.d(
+ TAG,
+ "id "
+ + id
+ + " is part of dataset but initial value "
+ + "didn't change: "
+ + value);
}
changed = false;
} else {
@@ -3833,8 +4204,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (changed) {
if (sDebug) {
- Slog.d(TAG, "found a change on required " + id + ": " + filledValue
- + " => " + value);
+ Slog.d(
+ TAG,
+ "found a change on required "
+ + id
+ + ": "
+ + filledValue
+ + " => "
+ + value);
}
atLeastOneChanged = true;
}
@@ -3844,8 +4221,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final AutofillId[] optionalIds = saveInfo.getOptionalIds();
if (sVerbose) {
- Slog.v(TAG, "allRequiredAreNotEmpty: " + allRequiredAreNotEmpty + " hasOptional: "
- + (optionalIds != null));
+ Slog.v(
+ TAG,
+ "allRequiredAreNotEmpty: "
+ + allRequiredAreNotEmpty
+ + " hasOptional: "
+ + (optionalIds != null));
}
int saveDialogNotShowReason;
if (!allRequiredAreNotEmpty) {
@@ -3859,7 +4240,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// - if at least one required id changed but it was not part of a filled dataset, we
// need to check if an optional id is part of a filled datased (in which case we show
// Update instead of Save)
- if (optionalIds!= null && (!atLeastOneChanged || !isUpdate)) {
+ if (optionalIds != null && (!atLeastOneChanged || !isUpdate)) {
// No change on required ids yet, look for changes on optional ids.
for (int i = 0; i < optionalIds.length; i++) {
final AutofillId id = optionalIds[i];
@@ -3879,8 +4260,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
viewState.getCandidateSaveValue();
if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) {
if (sVerbose) {
- Slog.v(TAG, "current value is empty, using cached last "
- + "non-empty value instead");
+ Slog.v(
+ TAG,
+ "current value is empty, using cached last "
+ + "non-empty value instead");
}
currentValue = candidateSaveValue;
}
@@ -3897,8 +4280,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final AutofillValue filledValue = viewState.getAutofilledValue();
if (value != null && !value.equals(filledValue)) {
if (sDebug) {
- Slog.d(TAG, "found a change on optional " + id + ": " + filledValue
- + " => " + value);
+ Slog.d(
+ TAG,
+ "found a change on optional "
+ + id
+ + ": "
+ + filledValue
+ + " => "
+ + value);
}
if (filledValue != null) {
isUpdate = true;
@@ -3907,12 +4296,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
atLeastOneChanged = true;
}
- } else {
+ } else {
// Update current values cache based on initial value
final AutofillValue initialValue = getValueFromContextsLocked(id);
if (sDebug) {
- Slog.d(TAG, "no current value for " + id + "; initial value is "
- + initialValue);
+ Slog.d(
+ TAG,
+ "no current value for "
+ + id
+ + "; initial value is "
+ + initialValue);
}
if (initialValue != null) {
currentValues.put(id, initialValue);
@@ -3935,17 +4328,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
try {
isValid = validator.isValid(this);
if (sDebug) Slog.d(TAG, validator + " returned " + isValid);
- log.setType(isValid
- ? MetricsEvent.TYPE_SUCCESS
- : MetricsEvent.TYPE_DISMISS);
+ log.setType(
+ isValid ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_DISMISS);
} catch (Exception e) {
Slog.e(TAG, "Not showing save UI because validation failed:", e);
log.setType(MetricsEvent.TYPE_FAILURE);
mMetricsLogger.write(log);
mSaveEventLogger.maybeSetSaveUiNotShownReason(
- NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
+ NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
mSaveEventLogger.logAndEndEvent();
- return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+ return new SaveResult(
+ /* logSaveShown= */ false,
+ /* removeSession= */ true,
Event.NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED);
}
@@ -3953,9 +4347,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (!isValid) {
Slog.i(TAG, "not showing save UI because fields failed validation");
mSaveEventLogger.maybeSetSaveUiNotShownReason(
- NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
+ NO_SAVE_REASON_FIELD_VALIDATION_FAILED);
mSaveEventLogger.logAndEndEvent();
- return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+ return new SaveResult(
+ /* logSaveShown= */ false,
+ /* removeSession= */ true,
Event.NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED);
}
}
@@ -3964,15 +4360,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// content.
final List<Dataset> datasets = response.getDatasets();
if (datasets != null) {
- datasets_loop: for (int i = 0; i < datasets.size(); i++) {
+ datasets_loop:
+ for (int i = 0; i < datasets.size(); i++) {
final Dataset dataset = datasets.get(i);
final ArrayMap<AutofillId, AutofillValue> datasetValues =
Helper.getFields(dataset);
if (sVerbose) {
- Slog.v(TAG, "Checking if saved fields match contents of dataset #" + i
- + ": " + dataset + "; savableIds=" + savableIds);
+ Slog.v(
+ TAG,
+ "Checking if saved fields match contents of dataset #"
+ + i
+ + ": "
+ + dataset
+ + "; savableIds="
+ + savableIds);
}
- savable_ids_loop: for (int j = 0; j < savableIds.size(); j++) {
+ savable_ids_loop:
+ for (int j = 0; j < savableIds.size(); j++) {
final AutofillId id = savableIds.valueAt(j);
final AutofillValue currentValue = currentValues.get(id);
if (currentValue == null) {
@@ -3984,20 +4388,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final AutofillValue datasetValue = datasetValues.get(id);
if (!currentValue.equals(datasetValue)) {
if (sDebug) {
- Slog.d(TAG, "found a dataset change on id " + id + ": from "
- + datasetValue + " to " + currentValue);
+ Slog.d(
+ TAG,
+ "found a dataset change on id "
+ + id
+ + ": from "
+ + datasetValue
+ + " to "
+ + currentValue);
}
continue datasets_loop;
}
if (sVerbose) Slog.v(TAG, "no dataset changes for id " + id);
}
if (sDebug) {
- Slog.d(TAG, "ignoring Save UI because all fields match contents of "
- + "dataset #" + i + ": " + dataset);
+ Slog.d(
+ TAG,
+ "ignoring Save UI because all fields match contents of "
+ + "dataset #"
+ + i
+ + ": "
+ + dataset);
}
mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_DATASET_MATCH);
mSaveEventLogger.logAndEndEvent();
- return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+ return new SaveResult(
+ /* logSaveShown= */ false,
+ /* removeSession= */ true,
Event.NO_SAVE_UI_REASON_DATASET_MATCH);
}
}
@@ -4015,13 +4432,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
wtf(null, "showSaveLocked(): no service label or icon");
mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE);
mSaveEventLogger.logAndEndEvent();
- return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
+ return new SaveResult(
+ /* logSaveShown= */ false,
+ /* removeSession= */ true,
Event.NO_SAVE_UI_REASON_NONE);
}
- getUiForShowing().showSaveUi(serviceLabel, serviceIcon,
- mService.getServicePackageName(), saveInfo, this,
- mComponentName, this, mContext, mPendingSaveUi, isUpdate, mCompatMode,
- response.getShowSaveDialogIcon(), mSaveEventLogger);
+ getUiForShowing()
+ .showSaveUi(
+ serviceLabel,
+ serviceIcon,
+ mService.getServicePackageName(),
+ saveInfo,
+ this,
+ mComponentName,
+ this,
+ mContext,
+ mPendingSaveUi,
+ isUpdate,
+ mCompatMode,
+ response.getShowSaveDialogIcon(),
+ mSaveEventLogger);
if (client != null) {
try {
client.setSaveUiState(id, true);
@@ -4031,21 +4461,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
mSessionFlags.mShowingSaveUi = true;
if (sDebug) {
- Slog.d(TAG, "Good news, everyone! All checks passed, show save UI for "
- + id + "!");
+ Slog.d(
+ TAG,
+ "Good news, everyone! All checks passed, show save UI for " + id + "!");
}
- return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false,
+ return new SaveResult(
+ /* logSaveShown= */ true,
+ /* removeSession= */ false,
Event.NO_SAVE_UI_REASON_NONE);
}
}
// Nothing changed...
if (sDebug) {
- Slog.d(TAG, "showSaveLocked(" + id +"): with no changes, comes no responsibilities."
- + "allRequiredAreNotNull=" + allRequiredAreNotEmpty
- + ", atLeastOneChanged=" + atLeastOneChanged);
+ Slog.d(
+ TAG,
+ "showSaveLocked("
+ + id
+ + "): with no changes, comes no responsibilities."
+ + "allRequiredAreNotNull="
+ + allRequiredAreNotEmpty
+ + ", atLeastOneChanged="
+ + atLeastOneChanged);
}
- return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true,
- saveDialogNotShowReason);
+ return new SaveResult(
+ /* logSaveShown= */ false, /* removeSession= */ true, saveDialogNotShowReason);
}
private void logSaveShown() {
@@ -4078,25 +4517,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return sanitized;
}
- /**
- * Returns whether the session is currently showing the save UI
- */
+ /** Returns whether the session is currently showing the save UI */
@GuardedBy("mLock")
boolean isSaveUiShowingLocked() {
return mSessionFlags.mShowingSaveUi;
}
- /**
- * Gets the latest non-empty value for the given id in the autofill contexts.
- */
+ /** Gets the latest non-empty value for the given id in the autofill contexts. */
@GuardedBy("mLock")
@Nullable
private ViewNode getViewNodeFromContextsLocked(@NonNull AutofillId autofillId) {
final int numContexts = mContexts.size();
for (int i = numContexts - 1; i >= 0; i--) {
final FillContext context = mContexts.get(i);
- final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(),
- autofillId);
+ final ViewNode node =
+ Helper.findViewNodeByAutofillId(context.getStructure(), autofillId);
if (node != null) {
return node;
}
@@ -4104,22 +4539,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return null;
}
- /**
- * Gets the latest non-empty value for the given id in the autofill contexts.
- */
+ /** Gets the latest non-empty value for the given id in the autofill contexts. */
@GuardedBy("mLock")
@Nullable
private AutofillValue getValueFromContextsLocked(@NonNull AutofillId autofillId) {
final int numContexts = mContexts.size();
for (int i = numContexts - 1; i >= 0; i--) {
final FillContext context = mContexts.get(i);
- final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(),
- autofillId);
+ final ViewNode node =
+ Helper.findViewNodeByAutofillId(context.getStructure(), autofillId);
if (node != null) {
final AutofillValue value = node.getAutofillValue();
if (sDebug) {
- Slog.d(TAG, "getValueFromContexts(" + this.id + "/" + autofillId + ") at "
- + i + ": " + value);
+ Slog.d(
+ TAG,
+ "getValueFromContexts("
+ + this.id
+ + "/"
+ + autofillId
+ + ") at "
+ + i
+ + ": "
+ + value);
}
if (value != null && !value.isEmpty()) {
return value;
@@ -4129,17 +4570,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return null;
}
- /**
- * Gets the latest autofill options for the given id in the autofill contexts.
- */
+ /** Gets the latest autofill options for the given id in the autofill contexts. */
@GuardedBy("mLock")
@Nullable
private CharSequence[] getAutofillOptionsFromContextsLocked(@NonNull AutofillId autofillId) {
final int numContexts = mContexts.size();
for (int i = numContexts - 1; i >= 0; i--) {
final FillContext context = mContexts.get(i);
- final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(),
- autofillId);
+ final ViewNode node =
+ Helper.findViewNodeByAutofillId(context.getStructure(), autofillId);
if (node != null && node.getAutofillOptions() != null) {
return node.getAutofillOptions();
}
@@ -4160,7 +4599,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final FillContext context = mContexts.get(contextNum);
final ViewNode[] nodes =
- context.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked());
+ context.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked());
if (sVerbose) Slog.v(TAG, "updateValuesForSaveLocked(): updating " + context);
@@ -4191,8 +4630,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sanitizedValue != null) {
node.updateAutofillValue(sanitizedValue);
} else if (sDebug) {
- Slog.d(TAG, "updateValuesForSaveLocked(): not updating field " + id
- + " because it failed sanitization");
+ Slog.d(
+ TAG,
+ "updateValuesForSaveLocked(): not updating field "
+ + id
+ + " because it failed sanitization");
}
}
@@ -4200,28 +4642,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
context.getStructure().sanitizeForParceling(false);
if (sVerbose) {
- Slog.v(TAG, "updateValuesForSaveLocked(): dumping structure of " + context
- + " before calling service.save()");
+ Slog.v(
+ TAG,
+ "updateValuesForSaveLocked(): dumping structure of "
+ + context
+ + " before calling service.save()");
context.getStructure().dump(false);
}
}
}
- /**
- * Calls service when user requested save.
- */
+ /** Calls service when user requested save. */
@GuardedBy("mLock")
void callSaveLocked() {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#callSaveLocked() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#callSaveLocked() rejected - session: " + id + " destroyed");
mSaveEventLogger.maybeSetIsSaved(false);
mSaveEventLogger.logAndEndEvent();
return;
}
if (mRemoteFillService == null) {
- wtf(null, "callSaveLocked() called without a remote service. "
- + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly);
+ wtf(
+ null,
+ "callSaveLocked() called without a remote service. "
+ + "mForAugmentedAutofillOnly: %s",
+ mSessionFlags.mAugmentedAutofillOnly);
mSaveEventLogger.maybeSetIsSaved(false);
mSaveEventLogger.logAndEndEvent();
return;
@@ -4241,7 +4688,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Remove pending fill requests as the session is finished.
cancelCurrentRequestLocked();
- final ArrayList<FillContext> contexts = mergePreviousSessionLocked( /* forSave= */ true);
+ final ArrayList<FillContext> contexts = mergePreviousSessionLocked(/* forSave= */ true);
FieldClassificationResponse fieldClassificationResponse =
mClassificationState.mLastFieldClassificationResponse;
@@ -4251,8 +4698,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (mClientState == null) {
mClientState = new Bundle();
}
- mClientState.putParcelableArrayList(EXTRA_KEY_DETECTIONS, new ArrayList<>(
- fieldClassificationResponse.getClassifications()));
+ mClientState.putParcelableArrayList(
+ EXTRA_KEY_DETECTIONS,
+ new ArrayList<>(fieldClassificationResponse.getClassifications()));
}
final SaveRequest saveRequest =
new SaveRequest(contexts, mClientState, mSelectedDatasetIds);
@@ -4270,11 +4718,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* from previous sessions that were asked by the service to be delayed (if any).
*
* <p>As a side-effect:
+ *
* <ul>
- * <li>If the current {@link #mClientState} is {@code null}, sets it with the last non-
- * {@code null} client state from previous sessions.
+ * <li>If the current {@link #mClientState} is {@code null}, sets it with the last non- {@code
+ * null} client state from previous sessions.
* <li>When {@code forSave} is {@code true}, calls {@link #updateValuesForSaveLocked()} in the
- * previous sessions.
+ * previous sessions.
* </ul>
*/
@NonNull
@@ -4283,30 +4732,51 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final ArrayList<FillContext> contexts;
if (previousSessions != null) {
if (sDebug) {
- Slog.d(TAG, "mergeSessions(" + this.id + "): Merging the content of "
- + previousSessions.size() + " sessions for task " + taskId);
+ Slog.d(
+ TAG,
+ "mergeSessions("
+ + this.id
+ + "): Merging the content of "
+ + previousSessions.size()
+ + " sessions for task "
+ + taskId);
}
contexts = new ArrayList<>();
for (int i = 0; i < previousSessions.size(); i++) {
final Session previousSession = previousSessions.get(i);
final ArrayList<FillContext> previousContexts = previousSession.mContexts;
if (previousContexts == null) {
- Slog.w(TAG, "mergeSessions(" + this.id + "): Not merging null contexts from "
- + previousSession.id);
+ Slog.w(
+ TAG,
+ "mergeSessions("
+ + this.id
+ + "): Not merging null contexts from "
+ + previousSession.id);
continue;
}
if (forSave) {
previousSession.updateValuesForSaveLocked();
}
if (sDebug) {
- Slog.d(TAG, "mergeSessions(" + this.id + "): adding " + previousContexts.size()
- + " context from previous session #" + previousSession.id);
+ Slog.d(
+ TAG,
+ "mergeSessions("
+ + this.id
+ + "): adding "
+ + previousContexts.size()
+ + " context from previous session #"
+ + previousSession.id);
}
contexts.addAll(previousContexts);
if (mClientState == null && previousSession.mClientState != null) {
if (sDebug) {
- Slog.d(TAG, "mergeSessions(" + this.id + "): setting client state from "
- + "previous session" + previousSession.id);
+ Slog.d(
+ TAG,
+ "mergeSessions("
+ + this.id
+ + "): setting client state from "
+ + "previous session"
+ + previousSession.id);
}
mClientState = previousSession.mClientState;
}
@@ -4351,8 +4821,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// If it's not, then check if it should start a partition.
if (shouldStartNewPartitionLocked(id, flags)) {
if (sDebug) {
- Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": "
- + viewState.getStateAsString());
+ Slog.d(
+ TAG,
+ "Starting partition or augmented request for view id "
+ + id
+ + ": "
+ + viewState.getStateAsString());
}
// Fix to always let standard autofill start.
// Sometimes activity contain IMPORTANT_FOR_AUTOFILL_NO fields which marks session as
@@ -4363,8 +4837,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (sVerbose) {
- Slog.v(TAG, "Not starting new partition for view " + id + ": "
- + viewState.getStateAsString());
+ Slog.v(
+ TAG,
+ "Not starting new partition for view "
+ + id
+ + ": "
+ + viewState.getStateAsString());
}
return Optional.empty();
}
@@ -4373,17 +4851,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* Determines if a new partition should be started for an id.
*
* @param id The id of the view that is entered
- *
* @return {@code true} if a new partition should be started
*/
@GuardedBy("mLock")
private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id, int flags) {
final ViewState currentView = mViewStates.get(id);
- SparseArray<FillResponse> responses = shouldRequestSecondaryProvider(flags)
- ? mSecondaryResponses : mResponses;
+ SparseArray<FillResponse> responses =
+ shouldRequestSecondaryProvider(flags) ? mSecondaryResponses : mResponses;
if (responses == null) {
- return currentView != null && (currentView.getState()
- & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) == 0;
+ return currentView != null
+ && (currentView.getState() & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST)
+ == 0;
}
if (mSessionFlags.mExpiredResponse) {
@@ -4395,8 +4873,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final int numResponses = responses.size();
if (numResponses >= AutofillManagerService.getPartitionMaxCount()) {
- Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id
- + " reached maximum of " + AutofillManagerService.getPartitionMaxCount());
+ Slog.e(
+ TAG,
+ "Not starting a new partition on "
+ + id
+ + " because session "
+ + this.id
+ + " reached maximum of "
+ + AutofillManagerService.getPartitionMaxCount());
return false;
}
@@ -4437,8 +4921,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
boolean shouldRequestSecondaryProvider(int flags) {
- if (!mService.isAutofillCredmanIntegrationEnabled()
- || mSecondaryProviderHandler == null) {
+ if (!mService.isAutofillCredmanIntegrationEnabled() || mSecondaryProviderHandler == null) {
return false;
}
if (mIsPrimaryCredential) {
@@ -4452,8 +4935,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// 'Session.this.mLock', which is the same as mLock.
@SuppressWarnings("GuardedBy")
@GuardedBy("mLock")
- void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int action,
- int flags) {
+ void updateLocked(
+ AutofillId id, Rect virtualBounds, AutofillValue value, int action, int flags) {
if (mDestroyed) {
Slog.w(TAG, "updateLocked(" + id + "): rejected - session: destroyed");
return;
@@ -4464,7 +4947,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Slog.d(TAG, "updateLocked(" + id + "): Set the response has expired.");
}
mPresentationStatsEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists(
- NOT_SHOWN_REASON_VIEW_CHANGED);
+ NOT_SHOWN_REASON_VIEW_CHANGED);
mPresentationStatsEventLogger.logAndEndEvent("ACTION_RESPONSE_EXPIRED");
return;
}
@@ -4474,23 +4957,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sVerbose) {
Slog.v(
TAG,
- "updateLocked(" + id + "): "
- + "id=" + this.id
- + ", action=" + actionAsString(action)
- + ", flags=" + flags
- + ", mCurrentViewId=" + mCurrentViewId
- + ", mExpiredResponse=" + mSessionFlags.mExpiredResponse
- + ", viewState=" + viewState);
+ "updateLocked("
+ + id
+ + "): "
+ + "id="
+ + this.id
+ + ", action="
+ + actionAsString(action)
+ + ", flags="
+ + flags
+ + ", mCurrentViewId="
+ + mCurrentViewId
+ + ", mExpiredResponse="
+ + mSessionFlags.mExpiredResponse
+ + ", viewState="
+ + viewState);
}
if (viewState == null) {
- if (action == ACTION_START_SESSION || action == ACTION_VALUE_CHANGED
+ if (action == ACTION_START_SESSION
+ || action == ACTION_VALUE_CHANGED
|| action == ACTION_VIEW_ENTERED) {
if (sVerbose) Slog.v(TAG, "Creating viewState for " + id);
boolean isIgnored = isIgnoredLocked(id);
- viewState = new ViewState(id, this,
- isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL,
- mIsPrimaryCredential);
+ viewState =
+ new ViewState(
+ id,
+ this,
+ isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL,
+ mIsPrimaryCredential);
mViewStates.put(id, viewState);
// TODO(b/73648631): for optimization purposes, should also ignore if change is
@@ -4521,7 +5016,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mSessionFlags.mScreenHasCredmanField = true;
}
- switch(action) {
+ switch (action) {
case ACTION_START_SESSION:
// View is triggering autofill.
mCurrentViewId = viewState.id;
@@ -4545,14 +5040,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
case ACTION_VALUE_CHANGED:
if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
// Must cancel the session if the value of the URL bar changed
- final String currentUrl = mUrlBar == null ? null
- : mUrlBar.getText().toString().trim();
+ final String currentUrl =
+ mUrlBar == null ? null : mUrlBar.getText().toString().trim();
if (currentUrl == null) {
// Validation check - shouldn't happen.
wtf(null, "URL bar value changed, but current value is null");
return;
}
- if (value == null || ! value.isText()) {
+ if (value == null || !value.isText()) {
// Validation check - shouldn't happen.
wtf(null, "URL bar value changed to null or non-text: %s", value);
return;
@@ -4567,8 +5062,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// are finished, as the URL bar changed callback is usually called before
// the virtual views become invisible.
if (sDebug) {
- Slog.d(TAG, "Ignoring change on URL because session will finish when "
- + "views are gone");
+ Slog.d(
+ TAG,
+ "Ignoring change on URL because session will finish when "
+ + "views are gone");
}
return;
}
@@ -4598,8 +5095,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// isSameViewEntered has some limitations, where it isn't considered same view when
// autofill suggestions pop up, user selects, and the focus lands back on the view.
// isSameViewAgain tries to overcome that situation.
- final boolean isSameViewAgain = isSameViewEntered
- || Objects.equals(mCurrentViewId, mPreviousNonNullEnteredViewId);
+ final boolean isSameViewAgain =
+ isSameViewEntered
+ || Objects.equals(mCurrentViewId, mPreviousNonNullEnteredViewId);
if (mCurrentViewId != null) {
mPreviousNonNullEnteredViewId = mCurrentViewId;
}
@@ -4655,8 +5153,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Trigger augmented autofill if applicable
if ((flags & FLAG_MANUAL_REQUEST) == 0) {
// Not a manual request
- if (mAugmentedAutofillableIds != null && mAugmentedAutofillableIds.contains(
- id)) {
+ if (mAugmentedAutofillableIds != null
+ && mAugmentedAutofillableIds.contains(id)) {
// Regular autofill handled the view and returned null response, but it
// triggered augmented autofill
if (!isSameViewEntered) {
@@ -4664,16 +5162,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
triggerAugmentedAutofillLocked(flags);
} else {
if (sDebug) {
- Slog.d(TAG, "skip augmented autofill for same view: "
- + "same view entered");
+ Slog.d(
+ TAG,
+ "skip augmented autofill for same view: "
+ + "same view entered");
}
}
return;
} else if (mSessionFlags.mAugmentedAutofillOnly && isSameViewEntered) {
// Regular autofill is disabled.
if (sDebug) {
- Slog.d(TAG, "skip augmented autofill for same view: "
- + "standard autofill disabled.");
+ Slog.d(
+ TAG,
+ "skip augmented autofill for same view: "
+ + "standard autofill disabled.");
}
return;
}
@@ -4717,8 +5219,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// on the IME side if it arrives before the input view is finished on the IME.
mInlineSessionController.resetInlineFillUiLocked();
- if ((viewState.getState() &
- ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) != 0) {
+ if ((viewState.getState() & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST)
+ != 0) {
// View was exited before Inline Request sent back, do not set it to
// null yet to let onHandleAssistData finish processing
} else {
@@ -4728,7 +5230,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// It's not necessary that there's no more presentation for this view. It could
// be that the user chose some suggestion, in which case, view exits.
mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
- NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
+ NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
}
break;
default:
@@ -4737,8 +5239,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
@GuardedBy("mLock")
- private void logPresentationStatsOnViewEnteredLocked(FillResponse response,
- boolean isCredmanRequested) {
+ private void logPresentationStatsOnViewEnteredLocked(
+ FillResponse response, boolean isCredmanRequested) {
mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
mFieldClassificationIdSnapshot);
@@ -4754,16 +5256,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private void hideAugmentedAutofillLocked(@NonNull ViewState viewState) {
- if ((viewState.getState()
- & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) {
+ if ((viewState.getState() & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) {
viewState.resetState(ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL);
cancelAugmentedAutofillLocked();
}
}
- /**
- * Checks whether a view should be ignored.
- */
+ /** Checks whether a view should be ignored. */
@GuardedBy("mLock")
private boolean isIgnoredLocked(AutofillId id) {
// Always check the latest response only
@@ -4782,22 +5281,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
&& getSaveInfoLocked() != null) {
final int length = viewState.getCurrentValue().getTextValue().length();
if (sDebug) {
- Slog.d(TAG, "updateLocked(" + id + "): resetting value that was "
- + length + " chars long");
- }
- final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length);
+ Slog.d(
+ TAG,
+ "updateLocked("
+ + id
+ + "): resetting value that was "
+ + length
+ + " chars long");
+ }
+ final LogMaker log =
+ newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length);
mMetricsLogger.write(log);
}
}
@GuardedBy("mLock")
- private void updateViewStateAndUiOnValueChangedLocked(AutofillId id, AutofillValue value,
- ViewState viewState, int flags) {
+ private void updateViewStateAndUiOnValueChangedLocked(
+ AutofillId id, AutofillValue value, ViewState viewState, int flags) {
// Cache the last non-empty value for save purpose. Some apps clear the form before
// navigating to other activities.
- if (mIgnoreViewStateResetToEmpty && (value == null || value.isEmpty())
- && viewState.getCurrentValue() != null && viewState.getCurrentValue().isText()
+ if (mIgnoreViewStateResetToEmpty
+ && (value == null || value.isEmpty())
+ && viewState.getCurrentValue() != null
+ && viewState.getCurrentValue().isText()
&& viewState.getCurrentValue().getTextValue() != null
&& viewState.getCurrentValue().getTextValue().length() > 1) {
if (sVerbose) {
@@ -4875,8 +5382,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* indicate the IME attempting to probe the potentially sensitive content of inline suggestions.
*/
@GuardedBy("mLock")
- private void updateFilteringStateOnValueChangedLocked(@Nullable String newTextValue,
- ViewState viewState) {
+ private void updateFilteringStateOnValueChangedLocked(
+ @Nullable String newTextValue, ViewState viewState) {
if (newTextValue == null) {
// Don't just return here, otherwise the IME can circumvent this logic using non-text
// values.
@@ -4901,18 +5408,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
@Override
- public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId,
- @Nullable AutofillValue value, int flags) {
+ public void onFillReady(
+ @NonNull FillResponse response,
+ @NonNull AutofillId filledId,
+ @Nullable AutofillValue value,
+ int flags) {
synchronized (mLock) {
mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
mFieldClassificationIdSnapshot);
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#onFillReady() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#onFillReady() rejected - session: " + id + " destroyed");
mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SESSION_DESTROYED);
mSaveEventLogger.logAndEndEvent();
mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
- NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY);
+ NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY);
mPresentationStatsEventLogger.logAndEndEvent("on fill ready");
return;
}
@@ -4954,7 +5465,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
} else {
setFillDialogDisabled();
}
-
}
if (response.supportsInlineSuggestions()) {
@@ -4971,10 +5481,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- getUiForShowing().showFillUi(filledId, response, filterText,
- mService.getServicePackageName(), mComponentName,
- serviceLabel, serviceIcon, this, mContext, id, mCompatMode,
- mService.getMaster().getMaxInputLengthForAutofill());
+ getUiForShowing()
+ .showFillUi(
+ filledId,
+ response,
+ filterText,
+ mService.getServicePackageName(),
+ mComponentName,
+ serviceLabel,
+ serviceIcon,
+ this,
+ mContext,
+ id,
+ mCompatMode,
+ mService.getMaster().getMaxInputLengthForAutofill());
synchronized (mLock) {
if (mUiShownTime == 0) {
@@ -4983,21 +5503,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final long duration = mUiShownTime - mStartTime;
if (sDebug) {
- final StringBuilder msg = new StringBuilder("1st UI for ")
- .append(mActivityToken)
- .append(" shown in ");
+ final StringBuilder msg =
+ new StringBuilder("1st UI for ")
+ .append(mActivityToken)
+ .append(" shown in ");
TimeUtils.formatDuration(duration, msg);
Slog.d(TAG, msg.toString());
}
- final StringBuilder historyLog = new StringBuilder("id=").append(id)
- .append(" app=").append(mActivityToken)
- .append(" svc=").append(mService.getServicePackageName())
- .append(" latency=");
+ final StringBuilder historyLog =
+ new StringBuilder("id=")
+ .append(id)
+ .append(" app=")
+ .append(mActivityToken)
+ .append(" svc=")
+ .append(mService.getServicePackageName())
+ .append(" latency=");
TimeUtils.formatDuration(duration, historyLog);
mUiLatencyHistory.log(historyLog.toString());
- addTaggedDataToRequestLogLocked(response.getRequestId(),
- MetricsEvent.FIELD_AUTOFILL_DURATION, duration);
+ addTaggedDataToRequestLogLocked(
+ response.getRequestId(), MetricsEvent.FIELD_AUTOFILL_DURATION, duration);
}
}
}
@@ -5052,8 +5577,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- private boolean requestShowFillDialog(FillResponse response,
- AutofillId filledId, String filterText, int flags) {
+ private boolean requestShowFillDialog(
+ FillResponse response, AutofillId filledId, String filterText, int flags) {
if (!isFillDialogUiEnabled()) {
// Unsupported fill dialog UI
if (sDebug) Log.w(TAG, "requestShowFillDialog: fill dialog is disabled");
@@ -5079,7 +5604,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sDebug) Log.w(TAG, "Last fill dialog triggered ids are changed.");
return false;
}
-
}
Drawable serviceIcon = null;
@@ -5087,21 +5611,31 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
serviceIcon = getServiceIcon(response);
}
- getUiForShowing().showFillDialog(filledId, response, filterText,
- mService.getServicePackageName(), mComponentName, serviceIcon, this,
- id, mCompatMode, mPresentationStatsEventLogger, mLock);
+ getUiForShowing()
+ .showFillDialog(
+ filledId,
+ response,
+ filterText,
+ mService.getServicePackageName(),
+ mComponentName,
+ serviceIcon,
+ this,
+ id,
+ mCompatMode,
+ mPresentationStatsEventLogger,
+ mLock);
return true;
}
/**
- * Get the custom icon that was passed through FillResponse. If the custom icon wasn't able
- * to be fetched, use the default provider icon instead
+ * Get the custom icon that was passed through FillResponse. If the custom icon wasn't able to
+ * be fetched, use the default provider icon instead
*
* @return Drawable of the provider icon, if it was able to be fetched. Null otherwise
*/
@SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's
- // actually the same object as mLock.
- // TODO: Expose mService.mLock or redesign instead.
+ // actually the same object as mLock.
+ // TODO: Expose mService.mLock or redesign instead.
@GuardedBy("mLock")
private Drawable getServiceIcon(FillResponse response) {
Drawable serviceIcon = null;
@@ -5130,14 +5664,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
- * Get the custom label that was passed through FillResponse. If the custom label
- * wasn't able to be fetched, use the default provider icon instead
+ * Get the custom label that was passed through FillResponse. If the custom label wasn't able to
+ * be fetched, use the default provider icon instead
*
* @return Drawable of the provider icon, if it was able to be fetched. Null otherwise
*/
@SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's
- // actually the same object as mLock.
- // TODO: Expose mService.mLock or redesign instead.
+ // actually the same object as mLock.
+ // TODO: Expose mService.mLock or redesign instead.
@GuardedBy("mLock")
private CharSequence getServiceLabel(FillResponse response) {
CharSequence serviceLabel = null;
@@ -5167,11 +5701,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return serviceLabel;
}
- /**
- * Returns whether we made a request to show inline suggestions.
- */
- private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
- @Nullable String filterText) {
+ /** Returns whether we made a request to show inline suggestions. */
+ private boolean requestShowInlineSuggestionsLocked(
+ @NonNull FillResponse response, @Nullable String filterText) {
if (mCurrentViewId == null) {
Log.w(TAG, "requestShowInlineSuggestionsLocked(): no view currently focused");
return false;
@@ -5199,89 +5731,122 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
final InlineFillUi.InlineFillUiInfo inlineFillUiInfo =
- new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId,
- filterText, remoteRenderService, userId, id);
- InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response,
- new InlineFillUi.InlineSuggestionUiCallback() {
- @Override
- public void autofill(@NonNull Dataset dataset, int datasetIndex) {
- fill(response.getRequestId(), datasetIndex, dataset, UI_TYPE_INLINE);
- }
+ new InlineFillUi.InlineFillUiInfo(
+ inlineSuggestionsRequest.get(),
+ focusedId,
+ filterText,
+ remoteRenderService,
+ userId,
+ id);
+ InlineFillUi inlineFillUi =
+ InlineFillUi.forAutofill(
+ inlineFillUiInfo,
+ response,
+ new InlineFillUi.InlineSuggestionUiCallback() {
+ @Override
+ public void autofill(@NonNull Dataset dataset, int datasetIndex) {
+ fill(
+ response.getRequestId(),
+ datasetIndex,
+ dataset,
+ UI_TYPE_INLINE);
+ }
- @Override
- public void authenticate(int requestId, int datasetIndex) {
- Session.this.authenticate(response.getRequestId(), datasetIndex,
- response.getAuthentication(), response.getClientState(),
- UI_TYPE_INLINE);
- }
+ @Override
+ public void authenticate(int requestId, int datasetIndex) {
+ Session.this.authenticate(
+ response.getRequestId(),
+ datasetIndex,
+ response.getAuthentication(),
+ response.getClientState(),
+ UI_TYPE_INLINE);
+ }
- @Override
- public void startIntentSender(@NonNull IntentSender intentSender) {
- Session.this.startIntentSender(intentSender, new Intent());
- }
+ @Override
+ public void startIntentSender(@NonNull IntentSender intentSender) {
+ Session.this.startIntentSender(intentSender, new Intent());
+ }
- @Override
- public void onError() {
- synchronized (mLock) {
- mInlineSessionController.setInlineFillUiLocked(
- InlineFillUi.emptyUi(focusedId));
- }
- }
+ @Override
+ public void onError() {
+ synchronized (mLock) {
+ mInlineSessionController.setInlineFillUiLocked(
+ InlineFillUi.emptyUi(focusedId));
+ }
+ }
- @Override
- public void onInflate() {
- Session.this.onShown(UI_TYPE_INLINE, 1);
- }
- }, mService.getMaster().getMaxInputLengthForAutofill());
+ @Override
+ public void onInflate() {
+ Session.this.onShown(UI_TYPE_INLINE, 1);
+ }
+ },
+ mService.getMaster().getMaxInputLengthForAutofill());
return mInlineSessionController.setInlineFillUiLocked(inlineFillUi);
}
private ResultReceiver constructCredentialManagerCallback(int requestId) {
- final ResultReceiver resultReceiver = new ResultReceiver(mHandler) {
- final AutofillId mAutofillId = mCurrentViewId;
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- if (resultCode == SUCCESS_CREDMAN_SELECTOR) {
- Slog.d(TAG, "onReceiveResult from Credential Manager "
- + "bottom sheet with mCurrentViewId: " + mAutofillId);
- GetCredentialResponse getCredentialResponse =
- resultData.getParcelable(
- CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
- GetCredentialResponse.class);
-
- if (Flags.autofillCredmanDevIntegration()) {
- sendCredentialManagerResponseToApp(getCredentialResponse,
- /*exception=*/ null, mAutofillId);
- } else {
- Dataset datasetFromCredential = getDatasetFromCredentialResponse(
- getCredentialResponse);
- if (datasetFromCredential != null) {
- autoFill(requestId, /*datasetIndex=*/-1,
- datasetFromCredential, false,
- UI_TYPE_CREDMAN_BOTTOM_SHEET);
+ final ResultReceiver resultReceiver =
+ new ResultReceiver(mHandler) {
+ final AutofillId mAutofillId = mCurrentViewId;
+
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode == SUCCESS_CREDMAN_SELECTOR) {
+ Slog.d(
+ TAG,
+ "onReceiveResult from Credential Manager "
+ + "bottom sheet with mCurrentViewId: "
+ + mAutofillId);
+ GetCredentialResponse getCredentialResponse =
+ resultData.getParcelable(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
+ GetCredentialResponse.class);
+
+ if (Flags.autofillCredmanDevIntegration()) {
+ sendCredentialManagerResponseToApp(
+ getCredentialResponse, /* exception= */ null, mAutofillId);
+ } else {
+ Dataset datasetFromCredential =
+ getDatasetFromCredentialResponse(getCredentialResponse);
+ if (datasetFromCredential != null) {
+ autoFill(
+ requestId,
+ /* datasetIndex= */ -1,
+ datasetFromCredential,
+ false,
+ UI_TYPE_CREDMAN_BOTTOM_SHEET);
+ }
+ }
+ } else if (resultCode == FAILURE_CREDMAN_SELECTOR) {
+ String[] exception =
+ resultData.getStringArray(
+ CredentialProviderService
+ .EXTRA_GET_CREDENTIAL_EXCEPTION);
+ if (exception != null && exception.length >= 2) {
+ String errType = exception[0];
+ String errMsg = exception[1];
+ Slog.w(
+ TAG,
+ "Credman bottom sheet from pinned "
+ + "entry failed with: + "
+ + errType
+ + " , "
+ + errMsg);
+ sendCredentialManagerResponseToApp(
+ /* response= */ null,
+ new GetCredentialException(errType, errMsg),
+ mAutofillId);
+ }
+ } else {
+ Slog.d(
+ TAG,
+ "Unknown resultCode from credential "
+ + "manager bottom sheet: "
+ + resultCode);
}
}
- } else if (resultCode == FAILURE_CREDMAN_SELECTOR) {
- String[] exception = resultData.getStringArray(
- CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION);
- if (exception != null && exception.length >= 2) {
- String errType = exception[0];
- String errMsg = exception[1];
- Slog.w(TAG, "Credman bottom sheet from pinned "
- + "entry failed with: + " + errType + " , "
- + errMsg);
- sendCredentialManagerResponseToApp(/*response=*/ null,
- new GetCredentialException(errType, errMsg),
- mAutofillId);
- }
- } else {
- Slog.d(TAG, "Unknown resultCode from credential "
- + "manager bottom sheet: " + resultCode);
- }
- }
- };
- ResultReceiver ipcFriendlyResultReceiver =
- toIpcFriendlyResultReceiver(resultReceiver);
+ };
+ ResultReceiver ipcFriendlyResultReceiver = toIpcFriendlyResultReceiver(resultReceiver);
return ipcFriendlyResultReceiver;
}
@@ -5309,8 +5874,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- private void notifyUnavailableToClient(int sessionFinishedState,
- @Nullable ArrayList<AutofillId> autofillableIds) {
+ private void notifyUnavailableToClient(
+ int sessionFinishedState, @Nullable ArrayList<AutofillId> autofillableIds) {
synchronized (mLock) {
if (mCurrentViewId == null) return;
try {
@@ -5374,27 +5939,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (saveInfo.getRequiredIds() != null) {
Collections.addAll(trackedViews, saveInfo.getRequiredIds());
mSaveEventLogger.maybeSetSaveUiShownReason(
- SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE);
+ SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE);
}
if (saveInfo.getOptionalIds() != null) {
Collections.addAll(trackedViews, saveInfo.getOptionalIds());
mSaveEventLogger.maybeSetSaveUiShownReason(
- SAVE_UI_SHOWN_REASON_OPTIONAL_ID_CHANGE);
+ SAVE_UI_SHOWN_REASON_OPTIONAL_ID_CHANGE);
}
}
if ((flags & SaveInfo.FLAG_DONT_SAVE_ON_FINISH) != 0) {
- mSaveEventLogger.maybeSetSaveUiShownReason(
- SAVE_UI_SHOWN_REASON_UNKNOWN);
+ mSaveEventLogger.maybeSetSaveUiShownReason(SAVE_UI_SHOWN_REASON_UNKNOWN);
mSaveEventLogger.maybeSetSaveUiNotShownReason(
- NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG);
+ NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG);
saveOnFinish = false;
}
} else {
flags = 0;
- mSaveEventLogger.maybeSetSaveUiNotShownReason(
- NO_SAVE_REASON_NO_SAVE_INFO);
+ mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NO_SAVE_INFO);
saveTriggerId = null;
}
@@ -5427,21 +5990,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
try {
if (sVerbose) {
- Slog.v(TAG, "updateTrackedIdsLocked(): trackedViews: " + trackedViews
- + " fillableIds: " + fillableIds + " triggerId: " + saveTriggerId
- + " saveOnFinish:" + saveOnFinish + " flags: " + flags
- + " hasSaveInfo: " + (saveInfo != null));
- }
- mClient.setTrackedViews(id, toArray(trackedViews), mSaveOnAllViewsInvisible,
- saveOnFinish, toArray(fillableIds), saveTriggerId, hasAuthentication);
+ Slog.v(
+ TAG,
+ "updateTrackedIdsLocked(): trackedViews: "
+ + trackedViews
+ + " fillableIds: "
+ + fillableIds
+ + " triggerId: "
+ + saveTriggerId
+ + " saveOnFinish:"
+ + saveOnFinish
+ + " flags: "
+ + flags
+ + " hasSaveInfo: "
+ + (saveInfo != null));
+ }
+ mClient.setTrackedViews(
+ id,
+ toArray(trackedViews),
+ mSaveOnAllViewsInvisible,
+ saveOnFinish,
+ toArray(fillableIds),
+ saveTriggerId,
+ hasAuthentication);
} catch (RemoteException e) {
Slog.w(TAG, "Cannot set tracked ids", e);
}
}
- /**
- * Sets the state of views that failed to autofill.
- */
+ /** Sets the state of views that failed to autofill. */
@GuardedBy("mLock")
void setAutofillFailureLocked(@NonNull List<AutofillId> ids, boolean isRefill) {
if (sVerbose && !ids.isEmpty()) {
@@ -5464,9 +6041,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mPresentationStatsEventLogger.maybeSetViewFillFailureCounts(ids, isRefill);
}
- /**
- * Sets the state of views that failed to autofill.
- */
+ /** Sets the state of views that failed to autofill. */
@GuardedBy("mLock")
void setViewAutofilledLocked(@NonNull AutofillId id) {
if (sVerbose) {
@@ -5478,17 +6053,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mPresentationStatsEventLogger.maybeAddSuccessId(id);
}
- /**
- * Sets the state of views that failed to autofill.
- */
+ /** Sets the state of views that failed to autofill. */
void setNotifyNotExpiringResponseDuringAuth() {
synchronized (mLock) {
mPresentationStatsEventLogger.maybeSetNotifyNotExpiringResponseDuringAuth();
}
}
- /**
- * Sets the state of views that failed to autofill.
- */
+
+ /** Sets the state of views that failed to autofill. */
void setLogViewEnteredIgnoredDuringAuth() {
synchronized (mLock) {
mPresentationStatsEventLogger.notifyViewEnteredIgnoredDuringAuthCount();
@@ -5496,10 +6068,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
@GuardedBy("mLock")
- private void replaceResponseLocked(@NonNull FillResponse oldResponse,
- @NonNull FillResponse newResponse, @Nullable Bundle newClientState) {
+ private void replaceResponseLocked(
+ @NonNull FillResponse oldResponse,
+ @NonNull FillResponse newResponse,
+ @Nullable Bundle newClientState) {
// Disassociate view states with the old response
- setViewStatesLocked(oldResponse, ViewState.STATE_INITIAL, /* clearResponse= */ true,
+ setViewStatesLocked(
+ oldResponse,
+ ViewState.STATE_INITIAL,
+ /* clearResponse= */ true,
/* isPrimary= */ true);
// Move over the id
newResponse.setRequestId(oldResponse.getRequestId());
@@ -5519,7 +6096,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final ArrayList<AutofillId> autofillableIds;
if (context != null) {
final AssistStructure structure = context.getStructure();
- autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */true);
+ autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */ true);
} else {
Slog.w(TAG, "processNullResponseLocked(): no context for req " + requestId);
autofillableIds = null;
@@ -5535,8 +6112,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(flags);
if (mAugmentedAutofillDestroyer == null && ((flags & FLAG_PASSWORD_INPUT_TYPE) == 0)) {
if (sVerbose) {
- Slog.v(TAG, "canceling session " + id + " when service returned null and it cannot "
- + "be augmented. AutofillableIds: " + autofillableIds);
+ Slog.v(
+ TAG,
+ "canceling session "
+ + id
+ + " when service returned null and it cannot "
+ + "be augmented. AutofillableIds: "
+ + autofillableIds);
}
// Nothing to be done, but need to notify client.
notifyUnavailableToClient(AutofillManager.STATE_FINISHED, autofillableIds);
@@ -5544,15 +6126,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
} else {
if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) {
if (sVerbose) {
- Slog.v(TAG, "keeping session " + id + " when service returned null and "
- + "augmented service is disabled for password fields. "
- + "AutofillableIds: " + autofillableIds);
+ Slog.v(
+ TAG,
+ "keeping session "
+ + id
+ + " when service returned null and "
+ + "augmented service is disabled for password fields. "
+ + "AutofillableIds: "
+ + autofillableIds);
}
mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
} else {
if (sVerbose) {
- Slog.v(TAG, "keeping session " + id + " when service returned null but "
- + "it can be augmented. AutofillableIds: " + autofillableIds);
+ Slog.v(
+ TAG,
+ "keeping session "
+ + id
+ + " when service returned null but "
+ + "it can be augmented. AutofillableIds: "
+ + autofillableIds);
}
}
mAugmentedAutofillableIds = autofillableIds;
@@ -5567,8 +6159,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/**
* Tries to trigger Augmented Autofill when the standard service could not fulfill a request.
*
- * <p> The request may not have been sent when this method returns as it may be waiting for
- * the inline suggestion request asynchronously.
+ * <p>The request may not have been sent when this method returns as it may be waiting for the
+ * inline suggestion request asynchronously.
*
* @return callback to destroy the autofill UI, or {@code null} if not supported.
*/
@@ -5582,8 +6174,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
// Check if Smart Suggestions is supported...
- final @SmartSuggestionMode int supportedModes = mService
- .getSupportedSmartSuggestionModesLocked();
+ final @SmartSuggestionMode int supportedModes =
+ mService.getSupportedSmartSuggestionModesLocked();
if (supportedModes == 0) {
if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no supported modes");
return null;
@@ -5591,8 +6183,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// ...then if the service is set for the user
- final RemoteAugmentedAutofillService remoteService = mService
- .getRemoteAugmentedAutofillServiceLocked();
+ final RemoteAugmentedAutofillService remoteService =
+ mService.getRemoteAugmentedAutofillServiceLocked();
if (remoteService == null) {
if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no service for user");
return null;
@@ -5612,25 +6204,37 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return null;
}
- final boolean isAllowlisted = mService
- .isWhitelistedForAugmentedAutofillLocked(mComponentName);
+ final boolean isAllowlisted =
+ mService.isWhitelistedForAugmentedAutofillLocked(mComponentName);
if (!isAllowlisted) {
if (sVerbose) {
- Slog.v(TAG, "triggerAugmentedAutofillLocked(): "
- + ComponentName.flattenToShortString(mComponentName) + " not whitelisted ");
- }
- logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(),
- mCurrentViewId, isAllowlisted, /* isInline= */ null);
+ Slog.v(
+ TAG,
+ "triggerAugmentedAutofillLocked(): "
+ + ComponentName.flattenToShortString(mComponentName)
+ + " not whitelisted ");
+ }
+ logAugmentedAutofillRequestLocked(
+ mode,
+ remoteService.getComponentName(),
+ mCurrentViewId,
+ isAllowlisted,
+ /* isInline= */ null);
return null;
}
if (sVerbose) {
- Slog.v(TAG, "calling Augmented Autofill Service ("
- + ComponentName.flattenToShortString(remoteService.getComponentName())
- + ") on view " + mCurrentViewId + " using suggestion mode "
- + getSmartSuggestionModeToString(mode)
- + " when server returned null for session " + this.id);
+ Slog.v(
+ TAG,
+ "calling Augmented Autofill Service ("
+ + ComponentName.flattenToShortString(remoteService.getComponentName())
+ + ") on view "
+ + mCurrentViewId
+ + " using suggestion mode "
+ + getSmartSuggestionModeToString(mode)
+ + " when server returned null for session "
+ + this.id);
}
// Log FillRequest for Augmented Autofill.
mFillRequestEventLogger.startLogForNewRequest();
@@ -5648,8 +6252,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (mAugmentedRequestsLogs == null) {
mAugmentedRequestsLogs = new ArrayList<>();
}
- final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_AUGMENTED_REQUEST,
- remoteService.getComponentName().getPackageName());
+ final LogMaker log =
+ newLogMaker(
+ MetricsEvent.AUTOFILL_AUGMENTED_REQUEST,
+ remoteService.getComponentName().getPackageName());
mAugmentedRequestsLogs.add(log);
final AutofillId focusedId = mCurrentViewId;
@@ -5715,8 +6321,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
synchronized (session.mLock) {
session.mInlineSessionController.onCreateInlineSuggestionsRequestLocked(
- mFocusedId, /*requestConsumer=*/ mRequestAugmentedAutofill,
- result);
+ mFocusedId, /* requestConsumer= */ mRequestAugmentedAutofill, result);
}
}
}
@@ -5741,19 +6346,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mIsAllowlisted = isAllowlisted;
mMode = mode;
mCurrentValue = currentValue;
-
}
+
@Override
public void accept(InlineSuggestionsRequest inlineSuggestionsRequest) {
Session session = mSessionWeakRef.get();
- if (logIfSessionNull(
- session, "AugmentedAutofillInlineSuggestionRequestConsumer:")) {
+ if (logIfSessionNull(session, "AugmentedAutofillInlineSuggestionRequestConsumer:")) {
return;
}
session.onAugmentedAutofillInlineSuggestionAccept(
inlineSuggestionsRequest, mFocusedId, mIsAllowlisted, mMode, mCurrentValue);
-
}
}
@@ -5770,8 +6373,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
public Boolean apply(InlineFillUi inlineFillUi) {
Session session = mSessionWeakRef.get();
- if (logIfSessionNull(
- session, "AugmentedAutofillInlineSuggestionsResponseCallback:")) {
+ if (logIfSessionNull(session, "AugmentedAutofillInlineSuggestionsResponseCallback:")) {
return false;
}
@@ -5801,8 +6403,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
- * If the session is null or has been destroyed, log the error msg, and return true.
- * This is a helper function intended to be called when de-referencing from a weak reference.
+ * If the session is null or has been destroyed, log the error msg, and return true. This is a
+ * helper function intended to be called when de-referencing from a weak reference.
+ *
* @param session
* @param logPrefix
* @return true if the session is null, false otherwise.
@@ -5830,15 +6433,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
synchronized (mLock) {
final RemoteAugmentedAutofillService remoteService =
mService.getRemoteAugmentedAutofillServiceLocked();
- logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(),
- focussedId, isAllowlisted, inlineSuggestionsRequest != null);
- remoteService.onRequestAutofillLocked(id, mClient,
- taskId, mComponentName, mActivityToken,
- AutofillId.withoutSession(focussedId), currentValue,
+ logAugmentedAutofillRequestLocked(
+ mode,
+ remoteService.getComponentName(),
+ focussedId,
+ isAllowlisted,
+ inlineSuggestionsRequest != null);
+ remoteService.onRequestAutofillLocked(
+ id,
+ mClient,
+ taskId,
+ mComponentName,
+ mActivityToken,
+ AutofillId.withoutSession(focussedId),
+ currentValue,
inlineSuggestionsRequest,
new AugmentedAutofillInlineSuggestionsResponseCallback(this),
new AugmentedAutofillErrorCallback(this),
- mService.getRemoteInlineSuggestionRenderServiceLocked(), userId);
+ mService.getRemoteInlineSuggestionRenderServiceLocked(),
+ userId);
}
}
@@ -5847,15 +6460,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
cancelAugmentedAutofillLocked();
// Also cancel augmented in IME
- mInlineSessionController.setInlineFillUiLocked(
- InlineFillUi.emptyUi(mCurrentViewId));
+ mInlineSessionController.setInlineFillUiLocked(InlineFillUi.emptyUi(mCurrentViewId));
}
}
@GuardedBy("mLock")
private void cancelAugmentedAutofillLocked() {
- final RemoteAugmentedAutofillService remoteService = mService
- .getRemoteAugmentedAutofillServiceLocked();
+ final RemoteAugmentedAutofillService remoteService =
+ mService.getRemoteAugmentedAutofillServiceLocked();
if (remoteService == null) {
Slog.w(TAG, "cancelAugmentedAutofillLocked(): no service for user");
return;
@@ -5865,8 +6477,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
@GuardedBy("mLock")
- private void processResponseLocked(@NonNull FillResponse newResponse,
- @Nullable Bundle newClientState, int flags) {
+ private void processResponseLocked(
+ @NonNull FillResponse newResponse, @Nullable Bundle newClientState, int flags) {
// Make sure we are hiding the UI which will be shown
// only if handling the current response requires it.
mUi.hideAll(this);
@@ -5878,9 +6490,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final int requestId = newResponse.getRequestId();
if (sVerbose) {
- Slog.v(TAG, "processResponseLocked(): mCurrentViewId=" + mCurrentViewId
- + ",flags=" + flags + ", reqId=" + requestId + ", resp=" + newResponse
- + ",newClientState=" + newClientState);
+ Slog.v(
+ TAG,
+ "processResponseLocked(): mCurrentViewId="
+ + mCurrentViewId
+ + ",flags="
+ + flags
+ + ", reqId="
+ + requestId
+ + ", resp="
+ + newResponse
+ + ",newClientState="
+ + newClientState);
}
if (mResponses == null) {
@@ -5892,8 +6513,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mResponses.put(requestId, newResponse);
mClientState = newClientState != null ? newClientState : newResponse.getClientState();
- boolean webviewRequestedCredman = newClientState != null && newClientState.getBoolean(
- WEBVIEW_REQUESTED_CREDENTIAL_KEY, false);
+ boolean webviewRequestedCredman =
+ newClientState != null
+ && newClientState.getBoolean(WEBVIEW_REQUESTED_CREDENTIAL_KEY, false);
List<Dataset> datasetList = newResponse.getDatasets();
mPresentationStatsEventLogger.maybeSetWebviewRequestedCredential(webviewRequestedCredman);
@@ -5901,7 +6523,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mPresentationStatsEventLogger.maybeSetAvailableCount(datasetList, mCurrentViewId);
mFillResponseEventLogger.maybeSetDatasetsCountAfterPotentialPccFiltering(datasetList);
- setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, /* clearResponse= */ false,
+ setViewStatesLocked(
+ newResponse,
+ ViewState.STATE_FILLABLE,
+ /* clearResponse= */ false,
/* isPrimary= */ true);
updateFillDialogTriggerIdsLocked();
updateTrackedIdsLocked();
@@ -5914,12 +6539,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
currentView.maybeCallOnFillReady(flags);
}
- /**
- * Sets the state of all views in the given response.
- */
+ /** Sets the state of all views in the given response. */
@GuardedBy("mLock")
- private void setViewStatesLocked(FillResponse response, int state, boolean clearResponse,
- boolean isPrimary) {
+ private void setViewStatesLocked(
+ FillResponse response, int state, boolean clearResponse, boolean isPrimary) {
final List<Dataset> datasets = response.getDatasets();
if (datasets != null && !datasets.isEmpty()) {
for (int i = 0; i < datasets.size(); i++) {
@@ -5964,12 +6587,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- /**
- * Sets the state and response of all views in the given dataset.
- */
+ /** Sets the state and response of all views in the given dataset. */
@GuardedBy("mLock")
- private void setViewStatesLocked(@Nullable FillResponse response, @NonNull Dataset dataset,
- int state, boolean clearResponse, boolean isPrimary) {
+ private void setViewStatesLocked(
+ @Nullable FillResponse response,
+ @NonNull Dataset dataset,
+ int state,
+ boolean clearResponse,
+ boolean isPrimary) {
final ArrayList<AutofillId> ids = dataset.getFieldIds();
final ArrayList<AutofillValue> values = dataset.getFieldValues();
for (int j = 0; j < ids.size(); j++) {
@@ -5989,10 +6614,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
@GuardedBy("mLock")
- private ViewState createOrUpdateViewStateLocked(@NonNull AutofillId id, int state,
- @Nullable AutofillValue value) {
+ private ViewState createOrUpdateViewStateLocked(
+ @NonNull AutofillId id, int state, @Nullable AutofillValue value) {
ViewState viewState = mViewStates.get(id);
- if (viewState != null) {
+ if (viewState != null) {
viewState.setState(state);
} else {
viewState = new ViewState(id, this, state, mIsPrimaryCredential);
@@ -6008,22 +6633,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return viewState;
}
- void autoFill(int requestId, int datasetIndex, Dataset dataset, boolean generateEvent,
- int uiType) {
+ void autoFill(
+ int requestId, int datasetIndex, Dataset dataset, boolean generateEvent, int uiType) {
if (sDebug) {
- Slog.d(TAG, "autoFill(): requestId=" + requestId + "; datasetIdx=" + datasetIndex
- + "; dataset=" + dataset);
+ Slog.d(
+ TAG,
+ "autoFill(): requestId="
+ + requestId
+ + "; datasetIdx="
+ + datasetIndex
+ + "; dataset="
+ + dataset);
}
synchronized (mLock) {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#autoFill() rejected - session: "
- + id + " destroyed");
+ Slog.w(TAG, "Call to Session#autoFill() rejected - session: " + id + " destroyed");
return;
}
// Selected dataset id is logged regardless of authentication result.
mPresentationStatsEventLogger.maybeSetSelectedDatasetId(datasetIndex);
mPresentationStatsEventLogger.maybeSetSelectedDatasetPickReason(
- dataset.getEligibleReason());
+ dataset.getEligibleReason());
// Autofill it directly...
if (dataset.getAuthentication() == null) {
if (generateEvent) {
@@ -6039,10 +6669,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// ...or handle authentication.
mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType);
mPresentationStatsEventLogger.maybeSetAuthenticationType(
- AUTHENTICATION_TYPE_DATASET_AUTHENTICATION);
+ AUTHENTICATION_TYPE_DATASET_AUTHENTICATION);
// does not matter the value of isPrimary because null response won't be overridden.
- setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH,
- /* clearResponse= */ false, /* isPrimary= */ true);
+ setViewStatesLocked(
+ null,
+ dataset,
+ ViewState.STATE_WAITING_DATASET_AUTH,
+ /* clearResponse= */ false,
+ /* isPrimary= */ true);
final Intent fillInIntent;
if (dataset.getCredentialFillInIntent() != null && Flags.autofillCredmanIntegration()) {
Slog.d(TAG, "Setting credential fill intent");
@@ -6055,11 +6689,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
forceRemoveFromServiceLocked();
return;
}
- final int authenticationId = AutofillManager.makeAuthenticationId(requestId,
- datasetIndex);
- startAuthentication(authenticationId, dataset.getAuthentication(), fillInIntent,
- /* authenticateInline= */false);
-
+ final int authenticationId =
+ AutofillManager.makeAuthenticationId(requestId, datasetIndex);
+ startAuthentication(
+ authenticationId,
+ dataset.getAuthentication(),
+ fillInIntent,
+ /* authenticateInline= */ false);
}
}
@@ -6072,13 +6708,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final FillContext context = getFillContextByRequestIdLocked(requestId);
if (context == null) {
- wtf(null, "createAuthFillInIntentLocked(): no FillContext. requestId=%d; mContexts=%s",
- requestId, mContexts);
+ wtf(
+ null,
+ "createAuthFillInIntentLocked(): no FillContext. requestId=%d; mContexts=%s",
+ requestId,
+ mContexts);
return null;
}
if (mLastInlineSuggestionsRequest != null
&& mLastInlineSuggestionsRequest.first == requestId) {
- fillInIntent.putExtra(AutofillManager.EXTRA_INLINE_SUGGESTIONS_REQUEST,
+ fillInIntent.putExtra(
+ AutofillManager.EXTRA_INLINE_SUGGESTIONS_REQUEST,
mLastInlineSuggestionsRequest.second);
}
fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure());
@@ -6121,12 +6761,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- private void startAuthentication(int authenticationId, IntentSender intent,
- Intent fillInIntent, boolean authenticateInline) {
+ private void startAuthentication(
+ int authenticationId,
+ IntentSender intent,
+ Intent fillInIntent,
+ boolean authenticateInline) {
try {
synchronized (mLock) {
- mClient.authenticate(id, authenticationId, intent, fillInIntent,
- authenticateInline);
+ mClient.authenticate(
+ id, authenticationId, intent, fillInIntent, authenticateInline);
}
} catch (RemoteException e) {
Slog.e(TAG, "Error launching auth intent", e);
@@ -6139,22 +6782,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* @hide
*/
static final class SaveResult {
- /**
- * Whether to record the save dialog has been shown.
- */
+ /** Whether to record the save dialog has been shown. */
private boolean mLogSaveShown;
- /**
- * Whether to remove the session.
- */
+ /** Whether to remove the session. */
private boolean mRemoveSession;
- /**
- * The reason why a save dialog was not shown.
- */
+ /** The reason why a save dialog was not shown. */
@NoSaveReason private int mSaveDialogNotShowReason;
- SaveResult(boolean logSaveShown, boolean removeSession,
+ SaveResult(
+ boolean logSaveShown,
+ boolean removeSession,
@NoSaveReason int saveDialogNotShowReason) {
mLogSaveShown = logSaveShown;
mRemoveSession = removeSession;
@@ -6218,15 +6857,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Override
public String toString() {
- return "SaveResult: [logSaveShown=" + mLogSaveShown
- + ", removeSession=" + mRemoveSession
- + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason + "]";
+ return "SaveResult: [logSaveShown="
+ + mLogSaveShown
+ + ", removeSession="
+ + mRemoveSession
+ + ", saveDialogNotShowReason="
+ + mSaveDialogNotShowReason
+ + "]";
}
}
/**
- * Class maintaining the state of the requests to
- * {@link android.service.assist.classification.FieldClassificationService}.
+ * Class maintaining the state of the requests to {@link
+ * android.service.assist.classification.FieldClassificationService}.
*/
private static final class ClassificationState {
@@ -6234,18 +6877,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
* Initial state indicating that the request for classification hasn't been triggered yet.
*/
private static final int STATE_INITIAL = 1;
- /**
- * Assist request has been triggered, but awaiting response.
- */
+
+ /** Assist request has been triggered, but awaiting response. */
private static final int STATE_PENDING_ASSIST_REQUEST = 2;
- /**
- * Classification request has been triggered, but awaiting response.
- */
+
+ /** Classification request has been triggered, but awaiting response. */
private static final int STATE_PENDING_REQUEST = 3;
- /**
- * Classification response has been received.
- */
+
+ /** Classification response has been received. */
private static final int STATE_RESPONSE = 4;
+
/**
* Classification state has been invalidated, and the last response may no longer be valid.
* This could occur due to various reasons like views changing their layouts, becoming
@@ -6254,15 +6895,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
*/
private static final int STATE_INVALIDATED = 5;
- @IntDef(prefix = { "STATE_" }, value = {
- STATE_INITIAL,
- STATE_PENDING_ASSIST_REQUEST,
- STATE_PENDING_REQUEST,
- STATE_RESPONSE,
- STATE_INVALIDATED
- })
+ @IntDef(
+ prefix = {"STATE_"},
+ value = {
+ STATE_INITIAL,
+ STATE_PENDING_ASSIST_REQUEST,
+ STATE_PENDING_REQUEST,
+ STATE_RESPONSE,
+ STATE_INVALIDATED
+ })
@Retention(RetentionPolicy.SOURCE)
- @interface ClassificationRequestState{}
+ @interface ClassificationRequestState {}
@GuardedBy("mLock")
private @ClassificationRequestState int mState = STATE_INITIAL;
@@ -6284,8 +6927,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/**
* Typically, there would be a 1:1 mapping. However, in certain cases, we may have a hint
- * being applicable to many types. An example of this being new/change password forms,
- * where you need to confirm the passward twice.
+ * being applicable to many types. An example of this being new/change password forms, where
+ * you need to confirm the passward twice.
*/
@GuardedBy("mLock")
private ArrayMap<String, Set<AutofillId>> mHintsToAutofillIdMap;
@@ -6317,8 +6960,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/**
* Process the response received.
+ *
* @return true if the response was processed, false otherwise. If there wasn't any
- * response, yet this function was called, it would return false.
+ * response, yet this function was called, it would return false.
*/
@GuardedBy("mLock")
private boolean processResponse() {
@@ -6358,7 +7002,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
@GuardedBy("mLock")
- private static void processDetections(Set<String> detections, AutofillId id,
+ private static void processDetections(
+ Set<String> detections,
+ AutofillId id,
ArrayMap<String, Set<AutofillId>> currentMap) {
for (String detection : detections) {
Set<AutofillId> autofillIds;
@@ -6416,37 +7062,67 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Override
public String toString() {
return "ClassificationState: ["
- + "state=" + stateToString()
- + ", mPendingFieldClassificationRequest=" + mPendingFieldClassificationRequest
- + ", mLastFieldClassificationResponse=" + mLastFieldClassificationResponse
- + ", mClassificationHintsMap=" + mClassificationHintsMap
- + ", mClassificationGroupHintsMap=" + mClassificationGroupHintsMap
- + ", mHintsToAutofillIdMap=" + mHintsToAutofillIdMap
- + ", mGroupHintsToAutofillIdMap=" + mGroupHintsToAutofillIdMap
+ + "state="
+ + stateToString()
+ + ", mPendingFieldClassificationRequest="
+ + mPendingFieldClassificationRequest
+ + ", mLastFieldClassificationResponse="
+ + mLastFieldClassificationResponse
+ + ", mClassificationHintsMap="
+ + mClassificationHintsMap
+ + ", mClassificationGroupHintsMap="
+ + mClassificationGroupHintsMap
+ + ", mHintsToAutofillIdMap="
+ + mHintsToAutofillIdMap
+ + ", mGroupHintsToAutofillIdMap="
+ + mGroupHintsToAutofillIdMap
+ "]";
}
-
}
@Override
public String toString() {
- return "Session: [id=" + id + ", component=" + mComponentName
- + ", state=" + sessionStateAsString(mSessionState) + "]";
+ return "Session: [id="
+ + id
+ + ", component="
+ + mComponentName
+ + ", state="
+ + sessionStateAsString(mSessionState)
+ + "]";
}
@GuardedBy("mLock")
void dumpLocked(String prefix, PrintWriter pw) {
final String prefix2 = prefix + " ";
- pw.print(prefix); pw.print("id: "); pw.println(id);
- pw.print(prefix); pw.print("uid: "); pw.println(uid);
- pw.print(prefix); pw.print("taskId: "); pw.println(taskId);
- pw.print(prefix); pw.print("flags: "); pw.println(mFlags);
- pw.print(prefix); pw.print("displayId: "); pw.println(mContext.getDisplayId());
- pw.print(prefix); pw.print("state: "); pw.println(sessionStateAsString(mSessionState));
- pw.print(prefix); pw.print("mComponentName: "); pw.println(mComponentName);
- pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
- pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime);
- pw.print(prefix); pw.print("Time to show UI: ");
+ pw.print(prefix);
+ pw.print("id: ");
+ pw.println(id);
+ pw.print(prefix);
+ pw.print("uid: ");
+ pw.println(uid);
+ pw.print(prefix);
+ pw.print("taskId: ");
+ pw.println(taskId);
+ pw.print(prefix);
+ pw.print("flags: ");
+ pw.println(mFlags);
+ pw.print(prefix);
+ pw.print("displayId: ");
+ pw.println(mContext.getDisplayId());
+ pw.print(prefix);
+ pw.print("state: ");
+ pw.println(sessionStateAsString(mSessionState));
+ pw.print(prefix);
+ pw.print("mComponentName: ");
+ pw.println(mComponentName);
+ pw.print(prefix);
+ pw.print("mActivityToken: ");
+ pw.println(mActivityToken);
+ pw.print(prefix);
+ pw.print("mStartTime: ");
+ pw.println(mStartTime);
+ pw.print(prefix);
+ pw.print("Time to show UI: ");
if (mUiShownTime == 0) {
pw.println("N/A");
} else {
@@ -6454,41 +7130,67 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
pw.println();
}
final int requestLogsSizes = mRequestLogs.size();
- pw.print(prefix); pw.print("mSessionLogs: "); pw.println(requestLogsSizes);
+ pw.print(prefix);
+ pw.print("mSessionLogs: ");
+ pw.println(requestLogsSizes);
for (int i = 0; i < requestLogsSizes; i++) {
final int requestId = mRequestLogs.keyAt(i);
final LogMaker log = mRequestLogs.valueAt(i);
- pw.print(prefix2); pw.print('#'); pw.print(i); pw.print(": req=");
- pw.print(requestId); pw.print(", log=" ); dumpRequestLog(pw, log); pw.println();
+ pw.print(prefix2);
+ pw.print('#');
+ pw.print(i);
+ pw.print(": req=");
+ pw.print(requestId);
+ pw.print(", log=");
+ dumpRequestLog(pw, log);
+ pw.println();
}
- pw.print(prefix); pw.print("mResponses: ");
+ pw.print(prefix);
+ pw.print("mResponses: ");
if (mResponses == null) {
pw.println("null");
} else {
pw.println(mResponses.size());
for (int i = 0; i < mResponses.size(); i++) {
- pw.print(prefix2); pw.print('#'); pw.print(i);
- pw.print(' '); pw.println(mResponses.valueAt(i));
- }
- }
- pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId);
- pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
- pw.print(prefix); pw.print("mShowingSaveUi: "); pw.println(mSessionFlags.mShowingSaveUi);
- pw.print(prefix); pw.print("mPendingSaveUi: "); pw.println(mPendingSaveUi);
+ pw.print(prefix2);
+ pw.print('#');
+ pw.print(i);
+ pw.print(' ');
+ pw.println(mResponses.valueAt(i));
+ }
+ }
+ pw.print(prefix);
+ pw.print("mCurrentViewId: ");
+ pw.println(mCurrentViewId);
+ pw.print(prefix);
+ pw.print("mDestroyed: ");
+ pw.println(mDestroyed);
+ pw.print(prefix);
+ pw.print("mShowingSaveUi: ");
+ pw.println(mSessionFlags.mShowingSaveUi);
+ pw.print(prefix);
+ pw.print("mPendingSaveUi: ");
+ pw.println(mPendingSaveUi);
final int numberViews = mViewStates.size();
- pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size());
+ pw.print(prefix);
+ pw.print("mViewStates size: ");
+ pw.println(mViewStates.size());
for (int i = 0; i < numberViews; i++) {
- pw.print(prefix); pw.print("ViewState at #"); pw.println(i);
+ pw.print(prefix);
+ pw.print("ViewState at #");
+ pw.println(i);
mViewStates.valueAt(i).dump(prefix2, pw);
}
- pw.print(prefix); pw.print("mContexts: " );
+ pw.print(prefix);
+ pw.print("mContexts: ");
if (mContexts != null) {
int numContexts = mContexts.size();
for (int i = 0; i < numContexts; i++) {
FillContext context = mContexts.get(i);
- pw.print(prefix2); pw.print(context);
+ pw.print(prefix2);
+ pw.print(context);
if (sVerbose) {
pw.println("AssistStructure dumped at logcat)");
@@ -6500,43 +7202,62 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
pw.println("null");
}
- pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback);
+ pw.print(prefix);
+ pw.print("mHasCallback: ");
+ pw.println(mHasCallback);
if (mClientState != null) {
- pw.print(prefix); pw.print("mClientState: "); pw.print(mClientState.getSize()); pw
- .println(" bytes");
- }
- pw.print(prefix); pw.print("mCompatMode: "); pw.println(mCompatMode);
- pw.print(prefix); pw.print("mUrlBar: ");
+ pw.print(prefix);
+ pw.print("mClientState: ");
+ pw.print(mClientState.getSize());
+ pw.println(" bytes");
+ }
+ pw.print(prefix);
+ pw.print("mCompatMode: ");
+ pw.println(mCompatMode);
+ pw.print(prefix);
+ pw.print("mUrlBar: ");
if (mUrlBar == null) {
pw.println("N/A");
} else {
- pw.print("id="); pw.print(mUrlBar.getAutofillId());
- pw.print(" domain="); pw.print(mUrlBar.getWebDomain());
- pw.print(" text="); Helper.printlnRedactedText(pw, mUrlBar.getText());
- }
- pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println(
- mSaveOnAllViewsInvisible);
- pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds);
+ pw.print("id=");
+ pw.print(mUrlBar.getAutofillId());
+ pw.print(" domain=");
+ pw.print(mUrlBar.getWebDomain());
+ pw.print(" text=");
+ Helper.printlnRedactedText(pw, mUrlBar.getText());
+ }
+ pw.print(prefix);
+ pw.print("mSaveOnAllViewsInvisible: ");
+ pw.println(mSaveOnAllViewsInvisible);
+ pw.print(prefix);
+ pw.print("mSelectedDatasetIds: ");
+ pw.println(mSelectedDatasetIds);
if (mSessionFlags.mAugmentedAutofillOnly) {
- pw.print(prefix); pw.println("For Augmented Autofill Only");
+ pw.print(prefix);
+ pw.println("For Augmented Autofill Only");
}
if (mSessionFlags.mFillDialogDisabled) {
- pw.print(prefix); pw.println("Fill Dialog disabled");
+ pw.print(prefix);
+ pw.println("Fill Dialog disabled");
}
if (mLastFillDialogTriggerIds != null) {
- pw.print(prefix); pw.println("Last Fill Dialog trigger ids: ");
+ pw.print(prefix);
+ pw.println("Last Fill Dialog trigger ids: ");
pw.println(mSelectedDatasetIds);
}
if (mAugmentedAutofillDestroyer != null) {
- pw.print(prefix); pw.println("has mAugmentedAutofillDestroyer");
+ pw.print(prefix);
+ pw.println("has mAugmentedAutofillDestroyer");
}
if (mAugmentedRequestsLogs != null) {
- pw.print(prefix); pw.print("number augmented requests: ");
+ pw.print(prefix);
+ pw.print("number augmented requests: ");
pw.println(mAugmentedRequestsLogs.size());
}
if (mAugmentedAutofillableIds != null) {
- pw.print(prefix); pw.print("mAugmentedAutofillableIds: ");
+ pw.print(prefix);
+ pw.print("mAugmentedAutofillableIds: ");
pw.println(mAugmentedAutofillableIds);
}
if (mRemoteFillService != null) {
@@ -6545,21 +7266,32 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
private static void dumpRequestLog(@NonNull PrintWriter pw, @NonNull LogMaker log) {
- pw.print("CAT="); pw.print(log.getCategory());
+ pw.print("CAT=");
+ pw.print(log.getCategory());
pw.print(", TYPE=");
final int type = log.getType();
switch (type) {
- case MetricsEvent.TYPE_SUCCESS: pw.print("SUCCESS"); break;
- case MetricsEvent.TYPE_FAILURE: pw.print("FAILURE"); break;
- case MetricsEvent.TYPE_CLOSE: pw.print("CLOSE"); break;
- default: pw.print("UNSUPPORTED");
- }
- pw.print('('); pw.print(type); pw.print(')');
- pw.print(", PKG="); pw.print(log.getPackageName());
- pw.print(", SERVICE="); pw.print(log
- .getTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE));
- pw.print(", ORDINAL="); pw.print(log
- .getTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL));
+ case MetricsEvent.TYPE_SUCCESS:
+ pw.print("SUCCESS");
+ break;
+ case MetricsEvent.TYPE_FAILURE:
+ pw.print("FAILURE");
+ break;
+ case MetricsEvent.TYPE_CLOSE:
+ pw.print("CLOSE");
+ break;
+ default:
+ pw.print("UNSUPPORTED");
+ }
+ pw.print('(');
+ pw.print(type);
+ pw.print(')');
+ pw.print(", PKG=");
+ pw.print(log.getPackageName());
+ pw.print(", SERVICE=");
+ pw.print(log.getTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE));
+ pw.print(", ORDINAL=");
+ pw.print(log.getTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL));
dumpNumericValue(pw, log, "FLAGS", MetricsEvent.FIELD_AUTOFILL_FLAGS);
dumpNumericValue(pw, log, "NUM_DATASETS", MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS);
dumpNumericValue(pw, log, "UI_LATENCY", MetricsEvent.FIELD_AUTOFILL_DURATION);
@@ -6569,64 +7301,86 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
pw.print(", AUTH_STATUS=");
switch (authStatus) {
case MetricsEvent.AUTOFILL_AUTHENTICATED:
- pw.print("AUTHENTICATED"); break;
+ pw.print("AUTHENTICATED");
+ break;
case MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED:
- pw.print("DATASET_AUTHENTICATED"); break;
+ pw.print("DATASET_AUTHENTICATED");
+ break;
case MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION:
- pw.print("INVALID_AUTHENTICATION"); break;
+ pw.print("INVALID_AUTHENTICATION");
+ break;
case MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION:
- pw.print("INVALID_DATASET_AUTHENTICATION"); break;
- default: pw.print("UNSUPPORTED");
+ pw.print("INVALID_DATASET_AUTHENTICATION");
+ break;
+ default:
+ pw.print("UNSUPPORTED");
}
- pw.print('('); pw.print(authStatus); pw.print(')');
+ pw.print('(');
+ pw.print(authStatus);
+ pw.print(')');
}
- dumpNumericValue(pw, log, "FC_IDS",
- MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS);
- dumpNumericValue(pw, log, "COMPAT_MODE",
- MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE);
+ dumpNumericValue(
+ pw, log, "FC_IDS", MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS);
+ dumpNumericValue(pw, log, "COMPAT_MODE", MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE);
}
- private static void dumpNumericValue(@NonNull PrintWriter pw, @NonNull LogMaker log,
- @NonNull String field, int tag) {
+ private static void dumpNumericValue(
+ @NonNull PrintWriter pw, @NonNull LogMaker log, @NonNull String field, int tag) {
final int value = getNumericValue(log, tag);
if (value != 0) {
- pw.print(", "); pw.print(field); pw.print('='); pw.print(value);
+ pw.print(", ");
+ pw.print(field);
+ pw.print('=');
+ pw.print(value);
}
}
- void sendCredentialManagerResponseToApp(@Nullable GetCredentialResponse response,
- @Nullable GetCredentialException exception, @NonNull AutofillId viewId) {
+ void sendCredentialManagerResponseToApp(
+ @Nullable GetCredentialResponse response,
+ @Nullable GetCredentialException exception,
+ @NonNull AutofillId viewId) {
synchronized (mLock) {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#sendCredentialManagerResponseToApp() rejected "
- + "- session: " + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#sendCredentialManagerResponseToApp() rejected "
+ + "- session: "
+ + id
+ + " destroyed");
return;
}
try {
final ViewState viewState = mViewStates.get(viewId);
if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly()
- && viewState != null && viewState.id.getSessionId() != id) {
+ && viewState != null
+ && viewState.id.getSessionId() != id) {
if (sVerbose) {
- Slog.v(TAG, "Skipping sending credential response to view: "
- + viewId + " as it isn't part of the current session: " + id);
+ Slog.v(
+ TAG,
+ "Skipping sending credential response to view: "
+ + viewId
+ + " as it isn't part of the current session: "
+ + id);
}
}
if (exception != null) {
if (viewId.isVirtualInt()) {
- sendResponseToViewNode(viewId, /*response=*/ null, exception);
+ sendResponseToViewNode(viewId, /* response= */ null, exception);
} else {
- mClient.onGetCredentialException(id, viewId, exception.getType(),
- exception.getMessage());
+ mClient.onGetCredentialException(
+ id, viewId, exception.getType(), exception.getMessage());
}
} else if (response != null) {
if (viewId.isVirtualInt()) {
- sendResponseToViewNode(viewId, response, /*exception=*/ null);
+ sendResponseToViewNode(viewId, response, /* exception= */ null);
} else {
mClient.onGetCredentialResponse(id, viewId, response);
}
} else {
- Slog.w(TAG, "sendCredentialManagerResponseToApp called with null response"
- + "and exception");
+ Slog.w(
+ TAG,
+ "sendCredentialManagerResponseToApp called with null response"
+ + "and exception");
}
} catch (RemoteException e) {
Slog.w(TAG, "Error sending credential response to activity: " + e);
@@ -6635,23 +7389,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
@GuardedBy("mLock")
- private void sendResponseToViewNode(AutofillId viewId, GetCredentialResponse response,
- GetCredentialException exception) {
+ private void sendResponseToViewNode(
+ AutofillId viewId, GetCredentialResponse response, GetCredentialException exception) {
ViewNode viewNode = getViewNodeFromContextsLocked(viewId);
if (viewNode != null && viewNode.getPendingCredentialCallback() != null) {
Bundle resultData = new Bundle();
if (response != null) {
resultData.putParcelable(
- CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
- response);
- viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR,
- resultData);
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, response);
+ viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR, resultData);
} else if (exception != null) {
resultData.putStringArray(
CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
new String[] {exception.getType(), exception.getMessage()});
- viewNode.getPendingCredentialCallback().send(FAILURE_CREDMAN_SELECTOR,
- resultData);
+ viewNode.getPendingCredentialCallback().send(FAILURE_CREDMAN_SELECTOR, resultData);
}
} else {
Slog.w(TAG, "View node not found after GetCredentialResponse");
@@ -6661,8 +7412,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
void autoFillApp(Dataset dataset) {
synchronized (mLock) {
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#autoFillApp() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#autoFillApp() rejected - session: " + id + " destroyed");
return;
}
try {
@@ -6671,8 +7423,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final List<AutofillId> ids = new ArrayList<>(entryCount);
final List<AutofillValue> values = new ArrayList<>(entryCount);
boolean waitingDatasetAuth = false;
- boolean hideHighlight = (entryCount == 1
- && dataset.getFieldIds().get(0).equals(mCurrentViewId));
+ boolean hideHighlight =
+ highlightAutofillSingleField()
+ ? false
+ : (entryCount == 1
+ && dataset.getFieldIds().get(0).equals(mCurrentViewId));
// Count how many views are filtered because they are not in current session
int numOfViewsFiltered = 0;
for (int i = 0; i < entryCount; i++) {
@@ -6682,10 +7437,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final AutofillId viewId = dataset.getFieldIds().get(i);
final ViewState viewState = mViewStates.get(viewId);
if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly()
- && viewState != null && viewState.id.getSessionId() != id) {
+ && viewState != null
+ && viewState.id.getSessionId() != id) {
if (sVerbose) {
- Slog.v(TAG, "Skipping filling view: " +
- viewId + " as it isn't part of the current session: " + id);
+ Slog.v(
+ TAG,
+ "Skipping filling view: "
+ + viewId
+ + " as it isn't part of the current session: "
+ + id);
}
numOfViewsFiltered += 1;
continue;
@@ -6721,8 +7481,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
// does not matter the value of isPrimary because null response won't be
// overridden.
- setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED,
- /* clearResponse= */ false, /* isPrimary= */ true);
+ setViewStatesLocked(
+ null,
+ dataset,
+ ViewState.STATE_AUTOFILLED,
+ /* clearResponse= */ false,
+ /* isPrimary= */ true);
}
} catch (RemoteException e) {
Slog.w(TAG, "Error autofilling activity: " + e);
@@ -6750,7 +7514,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mSessionCommittedEventLogger.maybeSetCommitReason(val);
mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
mSessionCommittedEventLogger.maybeSetSessionDurationMillis(
- SystemClock.elapsedRealtime() - mStartTime);
+ SystemClock.elapsedRealtime() - mStartTime);
mFillRequestEventLogger.logAndEndEvent();
mFillResponseEventLogger.logAndEndEvent();
mPresentationStatsEventLogger.logAndEndEvent("log all events");
@@ -6808,8 +7572,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- final int totalAugmentedRequests = mAugmentedRequestsLogs == null ? 0
- : mAugmentedRequestsLogs.size();
+ final int totalAugmentedRequests =
+ mAugmentedRequestsLogs == null ? 0 : mAugmentedRequestsLogs.size();
if (totalAugmentedRequests > 0) {
if (sVerbose) {
Slog.v(TAG, "destroyLocked(): logging " + totalRequests + " augmented requests");
@@ -6820,11 +7584,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_SESSION_FINISHED)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_REQUESTS, totalRequests);
+ final LogMaker log =
+ newLogMaker(MetricsEvent.AUTOFILL_SESSION_FINISHED)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_REQUESTS, totalRequests);
if (totalAugmentedRequests > 0) {
- log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS,
- totalAugmentedRequests);
+ log.addTaggedData(
+ MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS, totalAugmentedRequests);
}
if (mSessionFlags.mAugmentedAutofillOnly) {
log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_AUGMENTED_ONLY, 1);
@@ -6846,8 +7611,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
void forceRemoveFromServiceIfForAugmentedOnlyLocked() {
if (sVerbose) {
- Slog.v(TAG, "forceRemoveFromServiceIfForAugmentedOnlyLocked(" + this.id + "): "
- + mSessionFlags.mAugmentedAutofillOnly);
+ Slog.v(
+ TAG,
+ "forceRemoveFromServiceIfForAugmentedOnlyLocked("
+ + this.id
+ + "): "
+ + mSessionFlags.mAugmentedAutofillOnly);
}
if (!mSessionFlags.mAugmentedAutofillOnly) return;
@@ -6880,9 +7649,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- /**
- * Thread-safe version of {@link #removeFromServiceLocked()}.
- */
+ /** Thread-safe version of {@link #removeFromServiceLocked()}. */
private void removeFromService() {
synchronized (mLock) {
removeFromServiceLocked();
@@ -6897,8 +7664,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
void removeFromServiceLocked() {
if (sVerbose) Slog.v(TAG, "removeFromServiceLocked(" + this.id + "): " + mPendingSaveUi);
if (mDestroyed) {
- Slog.w(TAG, "Call to Session#removeFromServiceLocked() rejected - session: "
- + id + " destroyed");
+ Slog.w(
+ TAG,
+ "Call to Session#removeFromServiceLocked() rejected - session: "
+ + id
+ + " destroyed");
return;
}
if (isSaveUiPendingLocked()) {
@@ -6922,18 +7692,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
/**
- * Checks whether this session is hiding the Save UI to handle a custom description link for
- * a specific {@code token} created by
- * {@link PendingUi#PendingUi(IBinder, int, IAutoFillManagerClient)}.
+ * Checks whether this session is hiding the Save UI to handle a custom description link for a
+ * specific {@code token} created by {@link PendingUi#PendingUi(IBinder, int,
+ * IAutoFillManagerClient)}.
*/
@GuardedBy("mLock")
boolean isSaveUiPendingForTokenLocked(@NonNull IBinder token) {
return isSaveUiPendingLocked() && token.equals(mPendingSaveUi.getToken());
}
- /**
- * Checks whether this session is hiding the Save UI to handle a custom description link.
- */
+ /** Checks whether this session is hiding the Save UI to handle a custom description link. */
@GuardedBy("mLock")
private boolean isSaveUiPendingLocked() {
return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING;
@@ -6942,8 +7710,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// Return latest response index in mResponses SparseArray.
@GuardedBy("mLock")
private int getLastResponseIndexLocked() {
- if (mResponses == null || mResponses.size() == 0) {
- return -1;
+ if (mResponses == null || mResponses.size() == 0) {
+ return -1;
}
List<Integer> requestIdList = new ArrayList<>();
final int responseCount = mResponses.size();
@@ -6967,15 +7735,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private void logAuthenticationStatusLocked(int requestId, int status) {
- addTaggedDataToRequestLogLocked(requestId,
- MetricsEvent.FIELD_AUTOFILL_AUTHENTICATION_STATUS, status);
+ addTaggedDataToRequestLogLocked(
+ requestId, MetricsEvent.FIELD_AUTOFILL_AUTHENTICATION_STATUS, status);
}
@GuardedBy("mLock")
private void addTaggedDataToRequestLogLocked(int requestId, int tag, @Nullable Object value) {
final LogMaker requestLog = mRequestLogs.get(requestId);
if (requestLog == null) {
- Slog.w(TAG,
+ Slog.w(
+ TAG,
"addTaggedDataToRequestLogLocked(tag=" + tag + "): no log for id " + requestId);
return;
}
@@ -6983,20 +7752,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
@GuardedBy("mLock")
- private void logAugmentedAutofillRequestLocked(int mode,
- ComponentName augmentedRemoteServiceName, AutofillId focusedId, boolean isWhitelisted,
+ private void logAugmentedAutofillRequestLocked(
+ int mode,
+ ComponentName augmentedRemoteServiceName,
+ AutofillId focusedId,
+ boolean isWhitelisted,
Boolean isInline) {
final String historyItem =
- "aug:id=" + id + " u=" + uid + " m=" + mode
- + " a=" + ComponentName.flattenToShortString(mComponentName)
- + " f=" + focusedId
- + " s=" + augmentedRemoteServiceName
- + " w=" + isWhitelisted
- + " i=" + isInline;
+ "aug:id="
+ + id
+ + " u="
+ + uid
+ + " m="
+ + mode
+ + " a="
+ + ComponentName.flattenToShortString(mComponentName)
+ + " f="
+ + focusedId
+ + " s="
+ + augmentedRemoteServiceName
+ + " w="
+ + isWhitelisted
+ + " i="
+ + isInline;
mService.getMaster().logRequestLocked(historyItem);
}
- private void wtf(@Nullable Exception e, String fmt, Object...args) {
+ private void wtf(@Nullable Exception e, String fmt, Object... args) {
final String message = String.format(fmt, args);
synchronized (mLock) {
mWtfHistory.log(message);
@@ -7051,13 +7833,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mClassificationState.updateResponseReceived(response);
}
- public void onClassificationRequestFailure(int requestId, @Nullable CharSequence message) {
+ public void onClassificationRequestFailure(int requestId, @Nullable CharSequence message) {}
- }
-
- public void onClassificationRequestTimeout(int requestId) {
-
- }
+ public void onClassificationRequestTimeout(int requestId) {}
@Override
public void onServiceDied(@NonNull RemoteFieldClassificationService service) {
@@ -7070,12 +7848,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Override
public void logFieldClassificationEvent(
- long startTime, FieldClassificationResponse response,
+ long startTime,
+ FieldClassificationResponse response,
@FieldClassificationEventLogger.FieldClassificationStatus int status) {
final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger();
logger.startNewLogForRequest();
- logger.maybeSetLatencyMillis(
- SystemClock.elapsedRealtime() - startTime);
+ logger.maybeSetLatencyMillis(SystemClock.elapsedRealtime() - startTime);
logger.maybeSetAppPackageUid(uid);
logger.maybeSetNextFillRequestId(mFillRequestIdSnapshot + 1);
logger.maybeSetRequestId(sIdCounterForPcc.get());
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
index 57c643bb08a1..a7aab49cde56 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
@@ -160,11 +160,13 @@ public class ContentSuggestionsManagerService extends
HardwareBuffer snapshotBuffer = null;
int colorSpaceId = 0;
+ TaskSnapshot snapshot = null;
// Skip taking TaskSnapshot when bitmap is provided.
if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
// Can block, so call before acquiring the lock.
- TaskSnapshot snapshot =
- mActivityTaskManagerInternal.getTaskSnapshotBlocking(taskId, false);
+ snapshot = mActivityTaskManagerInternal.getTaskSnapshotBlocking(
+ taskId, false /* isLowResolution */,
+ TaskSnapshot.REFERENCE_CONTENT_SUGGESTION);
if (snapshot != null) {
snapshotBuffer = snapshot.getHardwareBuffer();
ColorSpace colorSpace = snapshot.getColorSpace();
@@ -185,6 +187,9 @@ public class ContentSuggestionsManagerService extends
}
}
}
+ if (snapshot != null) {
+ snapshot.removeReference(TaskSnapshot.REFERENCE_CONTENT_SUGGESTION);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 504137a29977..6a1e319b4039 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -46,6 +46,7 @@ import android.util.TimingsTraceLog;
import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.pm.RoSystemFeatures;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.build.UnboundedSdkLevel;
import com.android.server.pm.permission.PermissionAllowlist;
@@ -212,6 +213,30 @@ public class SystemConfig {
}
}
+ /**
+ * Utility class for testing interaction with compile-time defined system features.
+ * @hide
+ */
+ @VisibleForTesting
+ public static class Injector {
+ /** Whether a system feature is defined as enabled and available at compile-time. */
+ public boolean isReadOnlySystemEnabledFeature(String featureName, int version) {
+ return Boolean.TRUE.equals(RoSystemFeatures.maybeHasFeature(featureName, version));
+ }
+
+ /** Whether a system feature is defined as disabled and unavailable at compile-time. */
+ public boolean isReadOnlySystemDisabledFeature(String featureName, int version) {
+ return Boolean.FALSE.equals(RoSystemFeatures.maybeHasFeature(featureName, version));
+ }
+
+ /** The full set of system features defined as compile-time enabled and available. */
+ public ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+ return RoSystemFeatures.getReadOnlySystemEnabledFeatures();
+ }
+ }
+
+ private final Injector mInjector;
+
// These are the built-in shared libraries that were read from the
// system configuration files. Keys are the library names; values are
// the individual entries that contain information such as filename
@@ -220,7 +245,7 @@ public class SystemConfig {
// These are the features this devices supports that were read from the
// system configuration files.
- final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>();
+ final ArrayMap<String, FeatureInfo> mAvailableFeatures;
// These are the features which this device doesn't support; the OEM
// partition uses these to opt-out of features from the system image.
@@ -602,12 +627,26 @@ public class SystemConfig {
public ArrayMap<String, Integer> getOemDefinedUids() {
return mOemDefinedUids;
}
+
/**
* Only use for testing. Do NOT use in production code.
* @param readPermissions false to create an empty SystemConfig; true to read the permissions.
*/
@VisibleForTesting
public SystemConfig(boolean readPermissions) {
+ this(readPermissions, new Injector());
+ }
+
+ /**
+ * Only use for testing. Do NOT use in production code.
+ * @param readPermissions false to create an empty SystemConfig; true to read the permissions.
+ * @param injector Additional dependency injection for testing.
+ */
+ @VisibleForTesting
+ public SystemConfig(boolean readPermissions, Injector injector) {
+ mInjector = injector;
+ mAvailableFeatures = mInjector.getReadOnlySystemEnabledFeatures();
+
if (readPermissions) {
Slog.w(TAG, "Constructing a test SystemConfig");
readAllPermissions();
@@ -617,6 +656,9 @@ public class SystemConfig {
}
SystemConfig() {
+ mInjector = new Injector();
+ mAvailableFeatures = mInjector.getReadOnlySystemEnabledFeatures();
+
TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
log.traceBegin("readAllPermissions");
try {
@@ -1775,6 +1817,10 @@ public class SystemConfig {
}
private void addFeature(String name, int version) {
+ if (mInjector.isReadOnlySystemDisabledFeature(name, version)) {
+ Slog.w(TAG, "Skipping feature addition for compile-time disabled feature: " + name);
+ return;
+ }
FeatureInfo fi = mAvailableFeatures.get(name);
if (fi == null) {
fi = new FeatureInfo();
@@ -1787,6 +1833,10 @@ public class SystemConfig {
}
private void removeFeature(String name) {
+ if (mInjector.isReadOnlySystemEnabledFeature(name, /*version=*/0)) {
+ Slog.w(TAG, "Skipping feature removal for compile-time enabled feature: " + name);
+ return;
+ }
if (mAvailableFeatures.remove(name) != null) {
Slog.d(TAG, "Removed unavailable feature " + name);
}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index f8857d3e152a..d13dd2f2e1fc 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -443,6 +443,11 @@ final class UiModeManagerService extends SystemService {
mDreamsDisabledByAmbientModeSuppression = disabledByAmbientModeSuppression;
}
+ @VisibleForTesting
+ void setCurrentUser(int currentUserId) {
+ mCurrentUser = currentUserId;
+ }
+
@Override
public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
mCurrentUser = to.getUserIdentifier();
@@ -864,9 +869,9 @@ final class UiModeManagerService extends SystemService {
throw new IllegalArgumentException("Unknown mode: " + mode);
}
- final int user = UserHandle.getCallingUserId();
- enforceCurrentUserIfVisibleBackgroundEnabled(user);
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
+ final int user = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
@@ -929,7 +934,7 @@ final class UiModeManagerService extends SystemService {
@AttentionModeThemeOverlayType int attentionModeThemeOverlayType) {
setAttentionModeThemeOverlay_enforcePermission();
- enforceCurrentUserIfVisibleBackgroundEnabled(UserHandle.getCallingUserId());
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
synchronized (mLock) {
if (mAttentionModeThemeOverlay != attentionModeThemeOverlayType) {
@@ -1020,16 +1025,16 @@ final class UiModeManagerService extends SystemService {
return false;
}
final int user = Binder.getCallingUserHandle().getIdentifier();
- enforceCurrentUserIfVisibleBackgroundEnabled(user);
-
if (user != mCurrentUser && getContext().checkCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS)
!= PackageManager.PERMISSION_GRANTED) {
Slog.e(TAG, "Target user is not current user,"
+ " INTERACT_ACROSS_USERS permission is required");
return false;
-
}
+
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
+
// Store the last requested bedtime night mode state so that we don't need to notify
// anyone if the user decides to switch to the night mode to bedtime.
if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) {
@@ -1078,9 +1083,10 @@ final class UiModeManagerService extends SystemService {
Slog.e(TAG, "Set custom time start, requires MODIFY_DAY_NIGHT_MODE permission");
return;
}
- final int user = UserHandle.getCallingUserId();
- enforceCurrentUserIfVisibleBackgroundEnabled(user);
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
+
+ final int user = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
try {
LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
@@ -1108,9 +1114,10 @@ final class UiModeManagerService extends SystemService {
Slog.e(TAG, "Set custom time end, requires MODIFY_DAY_NIGHT_MODE permission");
return;
}
- final int user = UserHandle.getCallingUserId();
- enforceCurrentUserIfVisibleBackgroundEnabled(user);
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
+
+ final int user = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
try {
LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
@@ -1131,7 +1138,7 @@ final class UiModeManagerService extends SystemService {
assertLegit(callingPackage);
assertSingleProjectionType(projectionType);
enforceProjectionTypePermissions(projectionType);
- enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId());
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
synchronized (mLock) {
if (mProjectionHolders == null) {
@@ -1177,7 +1184,7 @@ final class UiModeManagerService extends SystemService {
assertLegit(callingPackage);
assertSingleProjectionType(projectionType);
enforceProjectionTypePermissions(projectionType);
- enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId());
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
return releaseProjectionUnchecked(projectionType, callingPackage);
}
@@ -1219,7 +1226,7 @@ final class UiModeManagerService extends SystemService {
return;
}
- enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId());
+ enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser);
synchronized (mLock) {
if (mProjectionListeners == null) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index f5a297bfd4f5..65a2c187f1c8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -246,7 +246,7 @@ final class ActivityManagerConstants extends ContentObserver {
static final int DEFAULT_MAX_SERVICE_CONNECTIONS_PER_PROCESS = 3000;
- private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = false;
+ private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = Flags.oomadjusterCachedAppTiers();
private static final long DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME = 60 * 1000;
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7f1d912d9a79..746c55f8fc9d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5542,7 +5542,6 @@ public class ActivityManagerService extends IActivityManager.Stub
public int sendIntentSender(IApplicationThread caller, IIntentSender target,
IBinder allowlistToken, int code, Intent intent, String resolvedType,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
- addCreatorToken(intent);
if (target instanceof PendingIntentRecord) {
final PendingIntentRecord originalRecord = (PendingIntentRecord) target;
@@ -5584,19 +5583,23 @@ public class ActivityManagerService extends IActivityManager.Stub
intent = new Intent(Intent.ACTION_MAIN);
}
try {
+ final int callingUid = Binder.getCallingUid();
+ final String packageName;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ packageName = AppGlobals.getPackageManager().getNameForUid(callingUid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
if (allowlistToken != null) {
- final int callingUid = Binder.getCallingUid();
- final String packageName;
- final long token = Binder.clearCallingIdentity();
- try {
- packageName = AppGlobals.getPackageManager().getNameForUid(callingUid);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
Slog.wtf(TAG, "Send a non-null allowlistToken to a non-PI target."
+ " Calling package: " + packageName + "; intent: " + intent
+ "; options: " + options);
}
+
+ addCreatorToken(intent, packageName);
+
target.send(code, intent, resolvedType, null, null,
requiredPermission, options);
} catch (RemoteException e) {
@@ -12371,7 +12374,7 @@ public class ActivityManagerService extends IActivityManager.Stub
continue;
}
endTime = SystemClock.currentThreadTimeMillis();
- hasSwapPss = mi.hasSwappedOutPss;
+ hasSwapPss = hasSwapPss || mi.hasSwappedOutPss;
memtrackGraphics = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GRAPHICS);
memtrackGl = mi.getOtherPrivate(Debug.MemoryInfo.OTHER_GL);
} else {
@@ -13049,7 +13052,7 @@ public class ActivityManagerService extends IActivityManager.Stub
continue;
}
endTime = SystemClock.currentThreadTimeMillis();
- hasSwapPss = mi.hasSwappedOutPss;
+ hasSwapPss = hasSwapPss || mi.hasSwappedOutPss;
} else {
reportType = ProcessStats.ADD_PSS_EXTERNAL;
startTime = SystemClock.currentThreadTimeMillis();
@@ -13628,7 +13631,7 @@ public class ActivityManagerService extends IActivityManager.Stub
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
enforceAllowedToStartOrBindServiceIfSdkSandbox(service);
- addCreatorToken(service);
+ addCreatorToken(service, callingPackage);
if (service != null) {
// Refuse possible leaked file descriptors
if (service.hasFileDescriptors()) {
@@ -13890,7 +13893,7 @@ public class ActivityManagerService extends IActivityManager.Stub
validateServiceInstanceName(instanceName);
- addCreatorToken(service);
+ addCreatorToken(service, callingPackage);
try {
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
final ComponentName cn = service.getComponent();
@@ -17174,7 +17177,7 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.v(TAG_SERVICE,
"startServiceInPackage: " + service + " type=" + resolvedType);
}
- addCreatorToken(service);
+ addCreatorToken(service, callingPackage);
final long origId = Binder.clearCallingIdentity();
ComponentName res;
try {
@@ -18002,8 +18005,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void addCreatorToken(Intent intent) {
- ActivityManagerService.this.addCreatorToken(intent);
+ public void addCreatorToken(Intent intent, String creatorPackage) {
+ ActivityManagerService.this.addCreatorToken(intent, creatorPackage);
}
}
@@ -19160,9 +19163,9 @@ public class ActivityManagerService extends IActivityManager.Stub
private final Key mKeyFields;
private final WeakReference<IntentCreatorToken> mRef;
- public IntentCreatorToken(int creatorUid, Intent intent) {
+ public IntentCreatorToken(int creatorUid, String creatorPackage, Intent intent) {
super();
- this.mKeyFields = new Key(creatorUid, intent);
+ this.mKeyFields = new Key(creatorUid, creatorPackage, intent);
mRef = new WeakReference<>(this);
}
@@ -19170,7 +19173,10 @@ public class ActivityManagerService extends IActivityManager.Stub
return mKeyFields.mCreatorUid;
}
- /** {@hide} */
+ public String getCreatorPackage() {
+ return mKeyFields.mCreatorPackage;
+ }
+
public static boolean isValid(@NonNull Intent intent) {
IBinder binder = intent.getCreatorToken();
IntentCreatorToken token = null;
@@ -19178,7 +19184,8 @@ public class ActivityManagerService extends IActivityManager.Stub
token = (IntentCreatorToken) binder;
}
return token != null && token.mKeyFields.equals(
- new Key(token.mKeyFields.mCreatorUid, intent));
+ new Key(token.mKeyFields.mCreatorUid, token.mKeyFields.mCreatorPackage,
+ intent));
}
@Override
@@ -19202,8 +19209,9 @@ public class ActivityManagerService extends IActivityManager.Stub
}
private static class Key {
- private Key(int creatorUid, Intent intent) {
+ private Key(int creatorUid, String creatorPackage, Intent intent) {
this.mCreatorUid = creatorUid;
+ this.mCreatorPackage = creatorPackage;
this.mAction = intent.getAction();
this.mData = intent.getData();
this.mType = intent.getType();
@@ -19220,6 +19228,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
private final int mCreatorUid;
+ private final String mCreatorPackage;
private final String mAction;
private final Uri mData;
private final String mType;
@@ -19233,17 +19242,20 @@ public class ActivityManagerService extends IActivityManager.Stub
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key key = (Key) o;
- return mCreatorUid == key.mCreatorUid && mFlags == key.mFlags && Objects.equals(
- mAction, key.mAction) && Objects.equals(mData, key.mData)
- && Objects.equals(mType, key.mType) && Objects.equals(mPackage,
- key.mPackage) && Objects.equals(mComponent, key.mComponent)
+ return mCreatorUid == key.mCreatorUid && mFlags == key.mFlags
+ && Objects.equals(mCreatorPackage, key.mCreatorPackage)
+ && Objects.equals(mAction, key.mAction)
+ && Objects.equals(mData, key.mData)
+ && Objects.equals(mType, key.mType)
+ && Objects.equals(mPackage, key.mPackage)
+ && Objects.equals(mComponent, key.mComponent)
&& Objects.equals(mClipDataUris, key.mClipDataUris);
}
@Override
public int hashCode() {
- return Objects.hash(mCreatorUid, mAction, mData, mType, mPackage, mComponent,
- mFlags, mClipDataUris);
+ return Objects.hash(mCreatorUid, mCreatorPackage, mAction, mData, mType, mPackage,
+ mComponent, mFlags, mClipDataUris);
}
}
}
@@ -19254,7 +19266,7 @@ public class ActivityManagerService extends IActivityManager.Stub
* @param intent The given intent
* @hide
*/
- public void addCreatorToken(@Nullable Intent intent) {
+ public void addCreatorToken(@Nullable Intent intent, String creatorPackage) {
if (!preventIntentRedirect()) return;
if (intent == null || intent.getExtraIntentKeys() == null) return;
@@ -19267,7 +19279,7 @@ public class ActivityManagerService extends IActivityManager.Stub
continue;
}
Slog.wtf(TAG, "A creator token is added to an intent.");
- IBinder creatorToken = createIntentCreatorToken(extraIntent);
+ IBinder creatorToken = createIntentCreatorToken(extraIntent, creatorPackage);
if (creatorToken != null) {
extraIntent.setCreatorToken(creatorToken);
}
@@ -19280,15 +19292,15 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- private IBinder createIntentCreatorToken(Intent intent) {
+ private IBinder createIntentCreatorToken(Intent intent, String creatorPackage) {
if (IntentCreatorToken.isValid(intent)) return null;
int creatorUid = getCallingUid();
- IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, intent);
+ IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, creatorPackage, intent);
IntentCreatorToken token;
synchronized (sIntentCreatorTokenCache) {
WeakReference<IntentCreatorToken> ref = sIntentCreatorTokenCache.get(key);
if (ref == null || ref.get() == null) {
- token = new IntentCreatorToken(creatorUid, intent);
+ token = new IntentCreatorToken(creatorUid, creatorPackage, intent);
sIntentCreatorTokenCache.put(key, token.mRef);
} else {
token = ref.get();
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index 15f1085b7125..a00cac6aba4f 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -258,6 +258,7 @@ class BroadcastController {
final StringBuilder sb = new StringBuilder("registerReceiver: ");
sb.append(Binder.getCallingUid()); sb.append('/');
sb.append(receiverId == null ? "null" : receiverId); sb.append('/');
+ sb.append("p:"); sb.append(filter.getPriority()); sb.append('/');
final int actionsCount = filter.safeCountActions();
if (actionsCount > 0) {
for (int i = 0; i < actionsCount; ++i) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 776a3455acc4..f60ee66cb236 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -3283,7 +3283,12 @@ public class OomAdjuster {
baseCapabilities = PROCESS_CAPABILITY_ALL; // BFSL allowed
break;
case PROCESS_STATE_BOUND_TOP:
- baseCapabilities = PROCESS_CAPABILITY_BFSL;
+ if (app.getActiveInstrumentation() != null) {
+ baseCapabilities = PROCESS_CAPABILITY_BFSL |
+ PROCESS_CAPABILITY_ALL_IMPLICIT;
+ } else {
+ baseCapabilities = PROCESS_CAPABILITY_BFSL;
+ }
break;
case PROCESS_STATE_FOREGROUND_SERVICE:
if (app.getActiveInstrumentation() != null) {
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 6857b6bcde15..3fb06a75d79f 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -432,6 +432,14 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
}
}
+ /**
+ * get package name of the PendingIntent sender.
+ * @return package name of the PendingIntent sender.
+ */
+ public String getPackageName() {
+ return key.packageName;
+ }
+
@Deprecated
public int sendInner(int code, Intent intent, String resolvedType, IBinder allowlistToken,
IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo,
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 5236b0399f25..cdb01889c139 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -813,10 +813,8 @@ public final class ProcessList {
private final Object mProcessChangeLock = new Object();
/**
- * All of the applications we currently have running organized by name.
- * The keys are strings of the application package name (as
- * returned by the package manager), and the keys are ApplicationRecord
- * objects.
+ * All of the processes that are running organized by name.
+ * The keys are process names and the values are the associated ProcessRecord objects.
*/
@CompositeRWLock({"mService", "mProcLock"})
private final MyProcessMap mProcessNames = new MyProcessMap();
@@ -5189,6 +5187,7 @@ public final class ProcessList {
if (ai != null) {
if (ai.packageName.equals(app.info.packageName)) {
app.info = ai;
+ app.getWindowProcessController().updateApplicationInfo(ai);
PlatformCompatCache.getInstance()
.onApplicationInfoChanged(ai);
}
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index adf0e640f6bf..d67fea09f945 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -225,3 +225,11 @@ flag {
description: "Migrate OomAdjuster pulled device state to a push model"
bug: "302575389"
}
+
+flag {
+ name: "oomadjuster_cached_app_tiers"
+ namespace: "system_performance"
+ is_fixed_read_only: true
+ description: "Assign cached oom_score_adj in tiers."
+ bug: "369893532"
+}
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
index 9a0ee034a8f2..a4804e1887fe 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java
@@ -50,8 +50,10 @@ public final class BrightnessReason {
public static final int MODIFIER_THROTTLED = 0x8;
public static final int MODIFIER_MIN_LUX = 0x10;
public static final int MODIFIER_MIN_USER_SET_LOWER_BOUND = 0x20;
+ public static final int MODIFIER_STYLUS_UNDER_USE = 0x40;
public static final int MODIFIER_MASK = MODIFIER_DIMMED | MODIFIER_LOW_POWER | MODIFIER_HDR
- | MODIFIER_THROTTLED | MODIFIER_MIN_LUX | MODIFIER_MIN_USER_SET_LOWER_BOUND;
+ | MODIFIER_THROTTLED | MODIFIER_MIN_LUX | MODIFIER_MIN_USER_SET_LOWER_BOUND
+ | MODIFIER_STYLUS_UNDER_USE;
// ADJUSTMENT_*
// These things can happen at any point, even if the main brightness reason doesn't
@@ -158,6 +160,9 @@ public final class BrightnessReason {
if ((mModifier & MODIFIER_MIN_USER_SET_LOWER_BOUND) != 0) {
sb.append(" user_min_pref");
}
+ if ((mModifier & MODIFIER_STYLUS_UNDER_USE) != 0) {
+ sb.append(" stylus_under_use");
+ }
int strlen = sb.length();
if (sb.charAt(strlen - 1) == '[') {
sb.setLength(strlen - 2);
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index 71fdaf3f85b6..4bd980822e46 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -110,6 +110,9 @@ public final class DisplayBrightnessController {
@VisibleForTesting
AutomaticBrightnessController mAutomaticBrightnessController;
+ // True if the stylus is being used
+ private boolean mIsStylusBeingUsed;
+
/**
* The constructor of DisplayBrightnessController.
*/
@@ -460,6 +463,8 @@ public final class DisplayBrightnessController {
writer.println(" mScreenBrightnessDefault=" + mScreenBrightnessDefault);
writer.println(" mPersistBrightnessNitsForDefaultDisplay="
+ mPersistBrightnessNitsForDefaultDisplay);
+ writer.println(" mIsStylusBeingUsed="
+ + mIsStylusBeingUsed);
synchronized (mLock) {
writer.println(" mPendingScreenBrightness=" + mPendingScreenBrightness);
writer.println(" mCurrentScreenBrightness=" + mCurrentScreenBrightness);
@@ -505,7 +510,12 @@ public final class DisplayBrightnessController {
* Notifies if the stylus is currently being used or not.
*/
public void setStylusBeingUsed(boolean isEnabled) {
- // Todo(b/369977976) - Disable the auto-brightness strategy
+ mIsStylusBeingUsed = isEnabled;
+ }
+
+ @VisibleForTesting
+ boolean isStylusBeingUsed() {
+ return mIsStylusBeingUsed;
}
@VisibleForTesting
@@ -626,13 +636,14 @@ public final class DisplayBrightnessController {
lastUserSetScreenBrightness = mLastUserSetScreenBrightness;
}
return new StrategySelectionRequest(displayPowerRequest, targetDisplayState,
- lastUserSetScreenBrightness, userSetBrightnessChanged, displayOffloadSession);
+ lastUserSetScreenBrightness, userSetBrightnessChanged, displayOffloadSession,
+ mIsStylusBeingUsed);
}
private StrategyExecutionRequest constructStrategyExecutionRequest(
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
float currentScreenBrightness = getCurrentBrightness();
return new StrategyExecutionRequest(displayPowerRequest, currentScreenBrightness,
- mUserSetScreenBrightnessUpdated);
+ mUserSetScreenBrightnessUpdated, mIsStylusBeingUsed);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index 06890e72f068..ded7447c5fbc 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -306,7 +306,8 @@ public class DisplayBrightnessStrategySelector {
strategySelectionRequest.getDisplayPowerRequest().useNormalBrightnessForDoze,
strategySelectionRequest.getLastUserSetScreenBrightness(),
strategySelectionRequest.isUserSetBrightnessChanged());
- return mAutomaticBrightnessStrategy1.isAutoBrightnessValid();
+ return !strategySelectionRequest.isStylusBeingUsed()
+ && mAutomaticBrightnessStrategy1.isAutoBrightnessValid();
}
private StrategySelectionNotifyRequest constructStrategySelectionNotifyRequest(
diff --git a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
index 304640b884ef..7a07c4fc22bf 100644
--- a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
+++ b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
@@ -32,11 +32,15 @@ public final class StrategyExecutionRequest {
// Represents if the user set screen brightness was changed or not.
private boolean mUserSetBrightnessChanged;
+ private boolean mIsStylusBeingUsed;
+
public StrategyExecutionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
- float currentScreenBrightness, boolean userSetBrightnessChanged) {
+ float currentScreenBrightness, boolean userSetBrightnessChanged,
+ boolean isStylusBeingUsed) {
mDisplayPowerRequest = displayPowerRequest;
mCurrentScreenBrightness = currentScreenBrightness;
mUserSetBrightnessChanged = userSetBrightnessChanged;
+ mIsStylusBeingUsed = isStylusBeingUsed;
}
public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() {
@@ -51,6 +55,10 @@ public final class StrategyExecutionRequest {
return mUserSetBrightnessChanged;
}
+ public boolean isStylusBeingUsed() {
+ return mIsStylusBeingUsed;
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof StrategyExecutionRequest)) {
@@ -59,12 +67,13 @@ public final class StrategyExecutionRequest {
StrategyExecutionRequest other = (StrategyExecutionRequest) obj;
return Objects.equals(mDisplayPowerRequest, other.getDisplayPowerRequest())
&& mCurrentScreenBrightness == other.getCurrentScreenBrightness()
- && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged();
+ && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged()
+ && mIsStylusBeingUsed == other.isStylusBeingUsed();
}
@Override
public int hashCode() {
return Objects.hash(mDisplayPowerRequest, mCurrentScreenBrightness,
- mUserSetBrightnessChanged);
+ mUserSetBrightnessChanged, mIsStylusBeingUsed);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java
index aa2f23ef9ec1..5c1f03d877a6 100644
--- a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java
+++ b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java
@@ -40,15 +40,19 @@ public final class StrategySelectionRequest {
private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+ private boolean mIsStylusBeingUsed;
+
public StrategySelectionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
int targetDisplayState, float lastUserSetScreenBrightness,
boolean userSetBrightnessChanged,
- DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) {
+ DisplayManagerInternal.DisplayOffloadSession displayOffloadSession,
+ boolean isStylusBeingUsed) {
mDisplayPowerRequest = displayPowerRequest;
mTargetDisplayState = targetDisplayState;
mLastUserSetScreenBrightness = lastUserSetScreenBrightness;
mUserSetBrightnessChanged = userSetBrightnessChanged;
mDisplayOffloadSession = displayOffloadSession;
+ mIsStylusBeingUsed = isStylusBeingUsed;
}
public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() {
@@ -72,6 +76,10 @@ public final class StrategySelectionRequest {
return mDisplayOffloadSession;
}
+ public boolean isStylusBeingUsed() {
+ return mIsStylusBeingUsed;
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof StrategySelectionRequest)) {
@@ -82,12 +90,14 @@ public final class StrategySelectionRequest {
&& mTargetDisplayState == other.getTargetDisplayState()
&& mLastUserSetScreenBrightness == other.getLastUserSetScreenBrightness()
&& mUserSetBrightnessChanged == other.isUserSetBrightnessChanged()
- && mDisplayOffloadSession.equals(other.getDisplayOffloadSession());
+ && mDisplayOffloadSession.equals(other.getDisplayOffloadSession())
+ && mIsStylusBeingUsed == other.isStylusBeingUsed();
}
@Override
public int hashCode() {
return Objects.hash(mDisplayPowerRequest, mTargetDisplayState,
- mLastUserSetScreenBrightness, mUserSetBrightnessChanged, mDisplayOffloadSession);
+ mLastUserSetScreenBrightness, mUserSetBrightnessChanged, mDisplayOffloadSession,
+ mIsStylusBeingUsed);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
index 7c0c9312888e..b9de34a80bda 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
@@ -37,6 +37,9 @@ public class FallbackBrightnessStrategy implements DisplayBrightnessStrategy{
StrategyExecutionRequest strategyExecutionRequest) {
BrightnessReason brightnessReason = new BrightnessReason();
brightnessReason.setReason(BrightnessReason.REASON_MANUAL);
+ if (strategyExecutionRequest.isStylusBeingUsed()) {
+ brightnessReason.setModifier(BrightnessReason.MODIFIER_STYLUS_UNDER_USE);
+ }
return new DisplayBrightnessState.Builder()
.setBrightness(strategyExecutionRequest.getCurrentScreenBrightness())
.setBrightnessReason(brightnessReason)
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 939aad469bd8..83044c202a04 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1536,7 +1536,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
}
final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
- return settings.getMethodMap().get(settings.getSelectedInputMethod());
+ final String selectedImeId;
+ if (Flags.consistentGetCurrentInputMethodInfo()) {
+ final var bindingController = getInputMethodBindingController(userId);
+ synchronized (ImfLock.class) {
+ selectedImeId = bindingController.getSelectedMethodId();
+ }
+ } else {
+ selectedImeId = settings.getSelectedInputMethod();
+ }
+ return settings.getMethodMap().get(selectedImeId);
}
@BinderThread
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 636854b85ee4..d1576c5cca4f 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -17,118 +17,63 @@
package com.android.server.integrity;
import static android.content.Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION;
-import static android.content.Intent.EXTRA_LONG_VERSION_CODE;
-import static android.content.Intent.EXTRA_ORIGINATING_UID;
-import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS;
import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE;
import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS;
-import static android.content.integrity.InstallerAllowedByManifestFormula.INSTALLER_CERTIFICATE_NOT_EVALUATED;
import static android.content.integrity.IntegrityUtils.getHexDigest;
import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
import android.annotation.BinderThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.integrity.AppInstallMetadata;
import android.content.integrity.IAppIntegrityManager;
import android.content.integrity.Rule;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
-import android.content.pm.Signature;
-import android.content.pm.SigningDetails;
-import android.content.pm.parsing.result.ParseResult;
-import android.content.pm.parsing.result.ParseTypeImpl;
import android.net.Uri;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.provider.Settings;
import android.util.Pair;
import android.util.Slog;
-import android.util.apk.SourceStampVerificationResult;
-import android.util.apk.SourceStampVerifier;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.pm.parsing.PackageParser2;
-import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
-import com.android.server.integrity.model.IntegrityCheckResult;
import com.android.server.integrity.model.RuleMetadata;
-import com.android.server.pm.PackageManagerServiceUtils;
-import com.android.server.pm.parsing.PackageParserUtils;
-import java.io.ByteArrayInputStream;
import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
-import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
/** Implementation of {@link AppIntegrityManagerService}. */
public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
- /**
- * This string will be used as the "installer" for formula evaluation when the app's installer
- * cannot be determined.
- *
- * <p>This may happen for various reasons. e.g., the installing app's package name may not match
- * its UID.
- */
- private static final String UNKNOWN_INSTALLER = "";
- /**
- * This string will be used as the "installer" for formula evaluation when the app is being
- * installed via ADB.
- */
- public static final String ADB_INSTALLER = "adb";
private static final String TAG = "AppIntegrityManagerServiceImpl";
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
- private static final String BASE_APK_FILE = "base.apk";
- private static final String ALLOWED_INSTALLERS_METADATA_NAME = "allowed-installers";
- private static final String ALLOWED_INSTALLER_DELIMITER = ",";
- private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|";
public static final boolean DEBUG_INTEGRITY_COMPONENT = false;
- private static final Set<String> PACKAGE_INSTALLER =
- new HashSet<>(
- Arrays.asList(
- "com.google.android.packageinstaller", "com.android.packageinstaller"));
-
// Access to files inside mRulesDir is protected by mRulesLock;
private final Context mContext;
private final Handler mHandler;
private final PackageManagerInternal mPackageManagerInternal;
- private final Supplier<PackageParser2> mParserSupplier;
private final IntegrityFileManager mIntegrityFileManager;
/** Create an instance of {@link AppIntegrityManagerServiceImpl}. */
@@ -139,7 +84,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
return new AppIntegrityManagerServiceImpl(
context,
LocalServices.getService(PackageManagerInternal.class),
- PackageParserUtils::forParsingFileWithDefaults,
IntegrityFileManager.getInstance(),
handlerThread.getThreadHandler());
}
@@ -148,12 +92,10 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
AppIntegrityManagerServiceImpl(
Context context,
PackageManagerInternal packageManagerInternal,
- Supplier<PackageParser2> parserSupplier,
IntegrityFileManager integrityFileManager,
Handler handler) {
mContext = context;
mPackageManagerInternal = packageManagerInternal;
- mParserSupplier = parserSupplier;
mIntegrityFileManager = integrityFileManager;
mHandler = handler;
@@ -263,148 +205,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
private void handleIntegrityVerification(Intent intent) {
int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1);
-
- try {
- if (DEBUG_INTEGRITY_COMPONENT) {
- Slog.d(TAG, "Received integrity verification intent " + intent.toString());
- Slog.d(TAG, "Extras " + intent.getExtras());
- }
-
- String installerPackageName = getInstallerPackageName(intent);
-
- // Skip integrity verification if the verifier is doing the install.
- if (!integrityCheckIncludesRuleProvider() && isRuleProvider(installerPackageName)) {
- if (DEBUG_INTEGRITY_COMPONENT) {
- Slog.i(TAG, "Verifier doing the install. Skipping integrity check.");
- }
- mPackageManagerInternal.setIntegrityVerificationResult(
- verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
- return;
- }
-
- String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
-
- Pair<SigningDetails, Bundle> packageSigningAndMetadata =
- getPackageSigningAndMetadata(intent.getData());
- if (packageSigningAndMetadata == null) {
- Slog.w(TAG, "Cannot parse package " + packageName);
- // We can't parse the package.
- mPackageManagerInternal.setIntegrityVerificationResult(
- verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
- return;
- }
-
- var signingDetails = packageSigningAndMetadata.first;
- List<String> appCertificates = getCertificateFingerprint(packageName, signingDetails);
- List<String> appCertificateLineage = getCertificateLineage(packageName, signingDetails);
- List<String> installerCertificates =
- getInstallerCertificateFingerprint(installerPackageName);
-
- AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder();
-
- builder.setPackageName(getPackageNameNormalized(packageName));
- builder.setAppCertificates(appCertificates);
- builder.setAppCertificateLineage(appCertificateLineage);
- builder.setVersionCode(intent.getLongExtra(EXTRA_LONG_VERSION_CODE, -1));
- builder.setInstallerName(getPackageNameNormalized(installerPackageName));
- builder.setInstallerCertificates(installerCertificates);
- builder.setIsPreInstalled(isSystemApp(packageName));
-
- Map<String, String> allowedInstallers =
- getAllowedInstallers(packageSigningAndMetadata.second);
- builder.setAllowedInstallersAndCert(allowedInstallers);
- extractSourceStamp(intent.getData(), builder);
-
- AppInstallMetadata appInstallMetadata = builder.build();
-
- if (DEBUG_INTEGRITY_COMPONENT) {
- Slog.i(
- TAG,
- "To be verified: "
- + appInstallMetadata
- + " installers "
- + allowedInstallers);
- }
- IntegrityCheckResult result = IntegrityCheckResult.allow();
- if (!result.getMatchedRules().isEmpty() || DEBUG_INTEGRITY_COMPONENT) {
- Slog.i(
- TAG,
- String.format(
- "Integrity check of %s result: %s due to %s",
- packageName, result.getEffect(), result.getMatchedRules()));
- }
-
- mPackageManagerInternal.setIntegrityVerificationResult(
- verificationId,
- result.getEffect() == IntegrityCheckResult.Effect.ALLOW
- ? PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW
- : PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
- } catch (IllegalArgumentException e) {
- // This exception indicates something is wrong with the input passed by package manager.
- // e.g., someone trying to trick the system. We block installs in this case.
- Slog.e(TAG, "Invalid input to integrity verification", e);
- mPackageManagerInternal.setIntegrityVerificationResult(
- verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
- } catch (Exception e) {
- // Other exceptions indicate an error within the integrity component implementation and
- // we allow them.
- Slog.e(TAG, "Error handling integrity verification", e);
- mPackageManagerInternal.setIntegrityVerificationResult(
- verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
- }
- }
-
- /**
- * Verify the UID and return the installer package name.
- *
- * @return the package name of the installer, or null if it cannot be determined or it is
- * installed via adb.
- */
- @Nullable
- private String getInstallerPackageName(Intent intent) {
- String installer =
- intent.getStringExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE);
- if (PackageManagerServiceUtils.isInstalledByAdb(installer)) {
- return ADB_INSTALLER;
- }
- int installerUid = intent.getIntExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID, -1);
- if (installerUid < 0) {
- Slog.e(
- TAG,
- "Installer cannot be determined: installer: "
- + installer
- + " installer UID: "
- + installerUid);
- return UNKNOWN_INSTALLER;
- }
-
- // Verify that the installer UID actually contains the package. Note that comparing UIDs
- // is not safe since context's uid can change in different settings; e.g. Android Auto.
- if (!getPackageListForUid(installerUid).contains(installer)) {
- return UNKNOWN_INSTALLER;
- }
-
- // At this time we can trust "installer".
-
- // A common way for apps to install packages is to send an intent to PackageInstaller. In
- // that case, the installer will always show up as PackageInstaller which is not what we
- // want.
- if (PACKAGE_INSTALLER.contains(installer)) {
- int originatingUid = intent.getIntExtra(EXTRA_ORIGINATING_UID, -1);
- if (originatingUid < 0) {
- Slog.e(TAG, "Installer is package installer but originating UID not found.");
- return UNKNOWN_INSTALLER;
- }
- List<String> installerPackages = getPackageListForUid(originatingUid);
- if (installerPackages.isEmpty()) {
- Slog.e(TAG, "No package found associated with originating UID " + originatingUid);
- return UNKNOWN_INSTALLER;
- }
- // In the case of multiple package sharing a UID, we just return the first one.
- return installerPackages.get(0);
- }
-
- return installer;
+ mPackageManagerInternal.setIntegrityVerificationResult(
+ verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
}
/** We will use the SHA256 digest of a package name if it is more than 32 bytes long. */
@@ -422,264 +224,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
}
}
- private List<String> getInstallerCertificateFingerprint(String installer) {
- if (installer.equals(ADB_INSTALLER) || installer.equals(UNKNOWN_INSTALLER)) {
- return Collections.emptyList();
- }
- var installerPkg = mPackageManagerInternal.getPackage(installer);
- if (installerPkg == null) {
- Slog.w(TAG, "Installer package " + installer + " not found.");
- return Collections.emptyList();
- }
- return getCertificateFingerprint(installerPkg.getPackageName(),
- installerPkg.getSigningDetails());
- }
-
- private List<String> getCertificateFingerprint(@NonNull String packageName,
- @NonNull SigningDetails signingDetails) {
- ArrayList<String> certificateFingerprints = new ArrayList();
- for (Signature signature : getSignatures(packageName, signingDetails)) {
- certificateFingerprints.add(getFingerprint(signature));
- }
- return certificateFingerprints;
- }
-
- private List<String> getCertificateLineage(@NonNull String packageName,
- @NonNull SigningDetails signingDetails) {
- ArrayList<String> certificateLineage = new ArrayList();
- for (Signature signature : getSignatureLineage(packageName, signingDetails)) {
- certificateLineage.add(getFingerprint(signature));
- }
- return certificateLineage;
- }
-
- /** Get the allowed installers and their associated certificate hashes from <meta-data> tag. */
- private Map<String, String> getAllowedInstallers(@Nullable Bundle metaData) {
- Map<String, String> packageCertMap = new HashMap<>();
- if (metaData != null) {
- String allowedInstallers = metaData.getString(ALLOWED_INSTALLERS_METADATA_NAME);
- if (allowedInstallers != null) {
- // parse the metadata for certs.
- String[] installerCertPairs = allowedInstallers.split(ALLOWED_INSTALLER_DELIMITER);
- for (String packageCertPair : installerCertPairs) {
- String[] packageAndCert =
- packageCertPair.split(INSTALLER_PACKAGE_CERT_DELIMITER);
- if (packageAndCert.length == 2) {
- String packageName = getPackageNameNormalized(packageAndCert[0]);
- String cert = packageAndCert[1];
- packageCertMap.put(packageName, cert);
- } else if (packageAndCert.length == 1) {
- packageCertMap.put(
- getPackageNameNormalized(packageAndCert[0]),
- INSTALLER_CERTIFICATE_NOT_EVALUATED);
- }
- }
- }
- }
-
- return packageCertMap;
- }
-
- /** Extract the source stamp embedded in the APK, if present. */
- private void extractSourceStamp(Uri dataUri, AppInstallMetadata.Builder appInstallMetadata) {
- File installationPath = getInstallationPath(dataUri);
- if (installationPath == null) {
- throw new IllegalArgumentException("Installation path is null, package not found");
- }
-
- SourceStampVerificationResult sourceStampVerificationResult;
- if (installationPath.isDirectory()) {
- try (Stream<Path> filesList = Files.list(installationPath.toPath())) {
- List<String> apkFiles =
- filesList
- .map(path -> path.toAbsolutePath().toString())
- .filter(str -> str.endsWith(".apk"))
- .collect(Collectors.toList());
- sourceStampVerificationResult = SourceStampVerifier.verify(apkFiles);
- } catch (IOException e) {
- throw new IllegalArgumentException("Could not read APK directory");
- }
- } else {
- sourceStampVerificationResult =
- SourceStampVerifier.verify(installationPath.getAbsolutePath());
- }
-
- appInstallMetadata.setIsStampPresent(sourceStampVerificationResult.isPresent());
- appInstallMetadata.setIsStampVerified(sourceStampVerificationResult.isVerified());
- // A verified stamp is set to be trusted.
- appInstallMetadata.setIsStampTrusted(sourceStampVerificationResult.isVerified());
- if (sourceStampVerificationResult.isVerified()) {
- X509Certificate sourceStampCertificate =
- (X509Certificate) sourceStampVerificationResult.getCertificate();
- // Sets source stamp certificate digest.
- try {
- MessageDigest digest = MessageDigest.getInstance("SHA-256");
- byte[] certificateDigest = digest.digest(sourceStampCertificate.getEncoded());
- appInstallMetadata.setStampCertificateHash(getHexDigest(certificateDigest));
- } catch (NoSuchAlgorithmException | CertificateEncodingException e) {
- throw new IllegalArgumentException(
- "Error computing source stamp certificate digest", e);
- }
- }
- }
-
- private static Signature[] getSignatures(@NonNull String packageName,
- @NonNull SigningDetails signingDetails) {
- Signature[] signatures = signingDetails.getSignatures();
- if (signatures == null || signatures.length < 1) {
- throw new IllegalArgumentException("Package signature not found in " + packageName);
- }
-
- // We are only interested in evaluating the active signatures.
- return signatures;
- }
-
- private static Signature[] getSignatureLineage(@NonNull String packageName,
- @NonNull SigningDetails signingDetails) {
- // Obtain the active signatures of the package.
- Signature[] signatureLineage = getSignatures(packageName, signingDetails);
-
- var pastSignatures = signingDetails.getPastSigningCertificates();
- // Obtain the past signatures of the package.
- if (signatureLineage.length == 1 && !ArrayUtils.isEmpty(pastSignatures)) {
- // Merge the signatures and return.
- Signature[] allSignatures =
- new Signature[signatureLineage.length + pastSignatures.length];
- int i;
- for (i = 0; i < signatureLineage.length; i++) {
- allSignatures[i] = signatureLineage[i];
- }
- for (int j = 0; j < pastSignatures.length; j++) {
- allSignatures[i] = pastSignatures[j];
- i++;
- }
- signatureLineage = allSignatures;
- }
-
- return signatureLineage;
- }
-
- private static String getFingerprint(Signature cert) {
- InputStream input = new ByteArrayInputStream(cert.toByteArray());
-
- CertificateFactory factory;
- try {
- factory = CertificateFactory.getInstance("X509");
- } catch (CertificateException e) {
- throw new RuntimeException("Error getting CertificateFactory", e);
- }
- X509Certificate certificate = null;
- try {
- if (factory != null) {
- certificate = (X509Certificate) factory.generateCertificate(input);
- }
- } catch (CertificateException e) {
- throw new RuntimeException("Error getting X509Certificate", e);
- }
-
- if (certificate == null) {
- throw new RuntimeException("X509 Certificate not found");
- }
-
- try {
- MessageDigest digest = MessageDigest.getInstance("SHA-256");
- byte[] publicKey = digest.digest(certificate.getEncoded());
- return getHexDigest(publicKey);
- } catch (NoSuchAlgorithmException | CertificateEncodingException e) {
- throw new IllegalArgumentException("Error error computing fingerprint", e);
- }
- }
-
- @Nullable
- private Pair<SigningDetails, Bundle> getPackageSigningAndMetadata(Uri dataUri) {
- File installationPath = getInstallationPath(dataUri);
- if (installationPath == null) {
- throw new IllegalArgumentException("Installation path is null, package not found");
- }
-
- try (PackageParser2 parser = mParserSupplier.get()) {
- var pkg = parser.parsePackage(installationPath, 0, false);
- // APK signatures is already verified elsewhere in PackageManager. We do not need to
- // verify it again since it could cause a timeout for large APKs.
- final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
- input, pkg, /* skipVerify= */ true);
- if (result.isError()) {
- Slog.w(TAG, result.getErrorMessage(), result.getException());
- return null;
- }
- return Pair.create(result.getResult(), pkg.getMetaData());
- } catch (Exception e) {
- Slog.w(TAG, "Exception reading " + dataUri, e);
- return null;
- }
- }
-
- private PackageInfo getMultiApkInfo(File multiApkDirectory) {
- // The base apk will normally be called base.apk
- File baseFile = new File(multiApkDirectory, BASE_APK_FILE);
- PackageInfo basePackageInfo =
- mContext.getPackageManager()
- .getPackageArchiveInfo(
- baseFile.getAbsolutePath(),
- PackageManager.GET_SIGNING_CERTIFICATES
- | PackageManager.GET_META_DATA);
-
- if (basePackageInfo == null) {
- for (File apkFile : multiApkDirectory.listFiles()) {
- if (apkFile.isDirectory()) {
- continue;
- }
-
- // If we didn't find a base.apk, then try to parse each apk until we find the one
- // that succeeds.
- try {
- basePackageInfo =
- mContext.getPackageManager()
- .getPackageArchiveInfo(
- apkFile.getAbsolutePath(),
- PackageManager.GET_SIGNING_CERTIFICATES
- | PackageManager.GET_META_DATA);
- } catch (Exception e) {
- // Some of the splits may not contain a valid android manifest. It is an
- // expected exception. We still log it nonetheless but we should keep looking.
- Slog.w(TAG, "Exception reading " + apkFile, e);
- }
- if (basePackageInfo != null) {
- Slog.i(TAG, "Found package info from " + apkFile);
- break;
- }
- }
- }
-
- if (basePackageInfo == null) {
- throw new IllegalArgumentException(
- "Base package info cannot be found from installation directory");
- }
-
- return basePackageInfo;
- }
-
- private File getInstallationPath(Uri dataUri) {
- if (dataUri == null) {
- throw new IllegalArgumentException("Null data uri");
- }
-
- String scheme = dataUri.getScheme();
- if (!"file".equalsIgnoreCase(scheme)) {
- throw new IllegalArgumentException("Unsupported scheme for " + dataUri);
- }
-
- File installationPath = new File(dataUri.getPath());
- if (!installationPath.exists()) {
- throw new IllegalArgumentException("Cannot find file for " + dataUri);
- }
- if (!installationPath.canRead()) {
- throw new IllegalArgumentException("Cannot read file for " + dataUri);
- }
- return installationPath;
- }
-
private String getCallerPackageNameOrThrow(int callingUid) {
String callerPackageName = getCallingRulePusherPackageName(callingUid);
if (callerPackageName == null) {
@@ -715,15 +259,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
return allowedCallingPackages.isEmpty() ? null : allowedCallingPackages.get(0);
}
- private boolean isRuleProvider(String installerPackageName) {
- for (String ruleProvider : getAllowedRuleProviderSystemApps()) {
- if (ruleProvider.matches(installerPackageName)) {
- return true;
- }
- }
- return false;
- }
-
private List<String> getAllowedRuleProviderSystemApps() {
List<String> integrityRuleProviders =
Arrays.asList(
@@ -751,14 +286,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
}
}
- private boolean integrityCheckIncludesRuleProvider() {
- return Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
- 0)
- == 1;
- }
-
private List<String> getPackageListForUid(int uid) {
try {
return Arrays.asList(mContext.getPackageManager().getPackagesForUid(uid));
diff --git a/services/core/java/com/android/server/media/AudioManagerRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java
index c79f41d32b29..25e1c94aa689 100644
--- a/services/core/java/com/android/server/media/AudioManagerRouteController.java
+++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java
@@ -420,7 +420,7 @@ import java.util.Objects;
// to derive a name ourselves from the type instead.
String deviceName = audioDeviceInfo.getPort().name();
- if (!TextUtils.isEmpty(address)) {
+ if (mBluetoothRouteController.containsBondedDeviceWithAddress(address)) {
routeId = mBluetoothRouteController.getRouteIdForBluetoothAddress(address);
deviceName = mBluetoothRouteController.getNameForBluetoothAddress(address);
}
diff --git a/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
index 8b65ea305ad8..c79d6e5400cd 100644
--- a/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
+++ b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java
@@ -141,6 +141,11 @@ import java.util.stream.Collectors;
mContext.unregisterReceiver(mDeviceStateChangedReceiver);
}
+ /** Returns true if the given address corresponds to a currently-bonded Bluetooth device. */
+ public synchronized boolean containsBondedDeviceWithAddress(@Nullable String address) {
+ return mAddressToBondedDevice.containsKey(address);
+ }
+
@Nullable
public synchronized String getRouteIdForBluetoothAddress(@Nullable String address) {
BluetoothDevice bluetoothDevice = mAddressToBondedDevice.get(address);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index d1d5d4868c6f..27bc1cf3e631 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -677,24 +677,15 @@ class MediaRouter2ServiceImpl {
@NonNull IMediaRouter2Manager manager,
int requestId,
@NonNull RoutingSessionInfo oldSession,
- @NonNull MediaRoute2Info route,
- @NonNull UserHandle transferInitiatorUserHandle,
- @NonNull String transferInitiatorPackageName) {
+ @NonNull MediaRoute2Info route) {
Objects.requireNonNull(manager, "manager must not be null");
Objects.requireNonNull(oldSession, "oldSession must not be null");
Objects.requireNonNull(route, "route must not be null");
- Objects.requireNonNull(transferInitiatorUserHandle);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- requestCreateSessionWithManagerLocked(
- requestId,
- manager,
- oldSession,
- route,
- transferInitiatorUserHandle,
- transferInitiatorPackageName);
+ requestCreateSessionWithManagerLocked(requestId, manager, oldSession, route);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -1738,9 +1729,7 @@ class MediaRouter2ServiceImpl {
int requestId,
@NonNull IMediaRouter2Manager manager,
@NonNull RoutingSessionInfo oldSession,
- @NonNull MediaRoute2Info route,
- @NonNull UserHandle transferInitiatorUserHandle,
- @NonNull String transferInitiatorPackageName) {
+ @NonNull MediaRoute2Info route) {
ManagerRecord managerRecord = mAllManagerRecords.get(manager.asBinder());
if (managerRecord == null) {
return;
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 363b8e4228b0..68e195d7f079 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -607,16 +607,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub
IMediaRouter2Manager manager,
int requestId,
RoutingSessionInfo oldSession,
- MediaRoute2Info route,
- UserHandle transferInitiatorUserHandle,
- String transferInitiatorPackageName) {
- mService2.requestCreateSessionWithManager(
- manager,
- requestId,
- oldSession,
- route,
- transferInitiatorUserHandle,
- transferInitiatorPackageName);
+ MediaRoute2Info route) {
+ mService2.requestCreateSessionWithManager(manager, requestId, oldSession, route);
}
// Binder call
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index a6f4c0e597d1..2a3be1e119bf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3100,11 +3100,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
synchronized (mUidRulesFirstLock) {
- final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
- policy |= oldPolicy;
- if (oldPolicy != policy) {
- setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
- mLogger.uidPolicyChanged(uid, oldPolicy, policy);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+ policy |= oldPolicy;
+ if (oldPolicy != policy) {
+ setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
+ mLogger.uidPolicyChanged(uid, oldPolicy, policy);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
}
@@ -3119,11 +3124,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
synchronized (mUidRulesFirstLock) {
- final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
- policy = oldPolicy & ~policy;
- if (oldPolicy != policy) {
- setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
- mLogger.uidPolicyChanged(uid, oldPolicy, policy);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final int oldPolicy = mUidPolicy.get(uid, POLICY_NONE);
+ policy = oldPolicy & ~policy;
+ if (oldPolicy != policy) {
+ setUidPolicyUncheckedUL(uid, oldPolicy, policy, true);
+ mLogger.uidPolicyChanged(uid, oldPolicy, policy);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index f3d6a2dd0a75..d5d4070ee4c3 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -44,7 +44,7 @@ public interface NotificationManagerInternal {
void onConversationRemoved(String pkg, int uid, Set<String> shortcuts);
- /** Get the number of notification channels for a given package */
+ /** Get the number of app created notification channels for a given package */
int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
/** Does the specified package/uid have permission to post notifications? */
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 828e02ce3edc..62d762244617 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4392,8 +4392,9 @@ public class NotificationManagerService extends SystemService {
List<NotificationChannel> channels = channelsList.getList();
final int channelsSize = channels.size();
ParceledListSlice<NotificationChannel> oldChannels =
- mPreferencesHelper.getNotificationChannels(pkg, uid, true);
- final boolean hadChannel = oldChannels != null && !oldChannels.getList().isEmpty();
+ mPreferencesHelper.getNotificationChannels(pkg, uid, true, false);
+ final boolean hadNonBundleChannel =
+ oldChannels != null && !oldChannels.getList().isEmpty();
boolean needsPolicyFileChange = false;
boolean hasRequestedNotificationPermission = false;
for (int i = 0; i < channelsSize; i++) {
@@ -4410,13 +4411,18 @@ public class NotificationManagerService extends SystemService {
mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(),
false),
NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
- boolean hasChannel = hadChannel || hasRequestedNotificationPermission;
- if (!hasChannel) {
+ boolean hasNonBundleChannel =
+ hadNonBundleChannel || hasRequestedNotificationPermission;
+ if (!hasNonBundleChannel) {
ParceledListSlice<NotificationChannel> currChannels =
- mPreferencesHelper.getNotificationChannels(pkg, uid, true);
- hasChannel = currChannels != null && !currChannels.getList().isEmpty();
- }
- if (!hadChannel && hasChannel && !hasRequestedNotificationPermission
+ mPreferencesHelper.getNotificationChannels(pkg, uid, true, false);
+ hasNonBundleChannel =
+ currChannels != null && !currChannels.getList().isEmpty();
+ }
+ // show perm prompt if new non-bundle channel added and the user has not
+ // seen the prompt
+ if (!hadNonBundleChannel && hasNonBundleChannel
+ && !hasRequestedNotificationPermission
&& startingTaskId != ActivityTaskManager.INVALID_TASK_ID) {
hasRequestedNotificationPermission = true;
if (mPermissionPolicyInternal == null) {
@@ -4651,7 +4657,7 @@ public class NotificationManagerService extends SystemService {
public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg,
int uid, boolean includeDeleted) {
enforceSystemOrSystemUI("getNotificationChannelsForPackage");
- return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted);
+ return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted, true);
}
@Override
@@ -4783,7 +4789,7 @@ public class NotificationManagerService extends SystemService {
/* ignore */
}
return mPreferencesHelper.getNotificationChannels(
- targetPkg, targetUid, false /* includeDeleted */);
+ targetPkg, targetUid, false /* includeDeleted */, true);
}
throw new SecurityException("Pkg " + callingPkg
+ " cannot read channels for " + targetPkg + " in " + userId);
@@ -6652,7 +6658,7 @@ public class NotificationManagerService extends SystemService {
verifyPrivilegedListener(token, user, true);
return mPreferencesHelper.getNotificationChannels(pkg,
- getUidForPackageAndUser(pkg, user), false /* includeDeleted */);
+ getUidForPackageAndUser(pkg, user), false /* includeDeleted */, true);
}
@Override
@@ -7693,8 +7699,9 @@ public class NotificationManagerService extends SystemService {
}
int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) {
- return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted).getList()
- .size();
+ // don't show perm prompt if the only channels are bundle channels
+ return mPreferencesHelper.getNotificationChannels(
+ pkg, uid, includeDeleted, false).getList().size();
}
void cancelNotificationInternal(String pkg, String opPkg, int callingUid, int callingPid,
@@ -8005,11 +8012,16 @@ public class NotificationManagerService extends SystemService {
}
/**
- * Returns a channel, if exists, and restores deleted conversation channels.
+ * Returns a channel, if exists and is not a bundle channel, and restores deleted
+ * conversation channels.
*/
@Nullable
private NotificationChannel getNotificationChannelRestoreDeleted(String pkg,
int callingUid, int notificationUid, String channelId, String conversationId) {
+ if (SYSTEM_RESERVED_IDS.contains(channelId)) {
+ // apps cannot post to these channels directly, in case they post incorrect content
+ return null;
+ }
// Restore a deleted conversation channel, if exists. Otherwise use the parent channel.
NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel(
pkg, notificationUid, channelId, conversationId,
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 3349b1308c3f..c9edc4106943 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1870,7 +1870,7 @@ public class PreferencesHelper implements RankingConfig {
@Override
public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
- boolean includeDeleted) {
+ boolean includeDeleted, boolean includeBundles) {
Objects.requireNonNull(pkg);
List<NotificationChannel> channels = new ArrayList<>();
synchronized (mLock) {
@@ -1882,7 +1882,9 @@ public class PreferencesHelper implements RankingConfig {
for (int i = 0; i < N; i++) {
final NotificationChannel nc = r.channels.valueAt(i);
if (includeDeleted || !nc.isDeleted()) {
- channels.add(nc);
+ if (includeBundles || !SYSTEM_RESERVED_IDS.contains(nc.getId())) {
+ channels.add(nc);
+ }
}
}
return new ParceledListSlice<>(channels);
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 8df24c9911a6..001e91cbea12 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -55,5 +55,5 @@ public interface RankingConfig {
void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannels(String pkg, int uid);
ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
- boolean includeDeleted);
+ boolean includeDeleted, boolean includeBundles);
}
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index a24c743929b5..f79d9ef174ea 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -165,6 +165,13 @@ flag {
}
flag {
+ name: "notification_lock_screen_settings"
+ namespace: "systemui"
+ description: "This flag enables the new settings page for the notifications on lock screen."
+ bug: "367455695"
+}
+
+flag {
name: "notification_vibration_in_sound_uri"
namespace: "systemui"
description: "This flag enables sound uri with vibration source"
diff --git a/services/core/java/com/android/server/pinner/PinnerService.java b/services/core/java/com/android/server/pinner/PinnerService.java
index d7ac5203ff53..2c75926c4943 100644
--- a/services/core/java/com/android/server/pinner/PinnerService.java
+++ b/services/core/java/com/android/server/pinner/PinnerService.java
@@ -121,9 +121,6 @@ public final class PinnerService extends SystemService {
private static boolean PROP_PIN_PINLIST =
SystemProperties.getBoolean("pinner.use_pinlist", true);
- private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app.
- private static final int MAX_ASSISTANT_PIN_SIZE = 60 * (1 << 20); // 60MB max for assistant app.
-
public static final String ANON_REGION_STAT_NAME = "[anon]";
private static final String SYSTEM_GROUP_NAME = "system";
@@ -179,8 +176,10 @@ public final class PinnerService extends SystemService {
// Resource-configured pinner flags;
private final boolean mConfiguredToPinCamera;
+ private final int mConfiguredCameraPinBytes;
private final int mConfiguredHomePinBytes;
private final boolean mConfiguredToPinAssistant;
+ private final int mConfiguredAssistantPinBytes;
private final int mConfiguredWebviewPinBytes;
// This is the percentage of total device memory that will be used to set the global quota.
@@ -250,6 +249,10 @@ public final class PinnerService extends SystemService {
mDeviceConfigInterface = mInjector.getDeviceConfigInterface();
mConfiguredToPinCamera = context.getResources().getBoolean(
com.android.internal.R.bool.config_pinnerCameraApp);
+ mConfiguredCameraPinBytes = context.getResources().getInteger(
+ com.android.internal.R.integer.config_pinnerCameraPinBytes);
+ mConfiguredAssistantPinBytes = context.getResources().getInteger(
+ com.android.internal.R.integer.config_pinnerAssistantPinBytes);
mConfiguredHomePinBytes = context.getResources().getInteger(
com.android.internal.R.integer.config_pinnerHomePinBytes);
mConfiguredToPinAssistant = context.getResources().getBoolean(
@@ -812,11 +815,11 @@ public final class PinnerService extends SystemService {
private int getSizeLimitForKey(@AppKey int key) {
switch (key) {
case KEY_CAMERA:
- return MAX_CAMERA_PIN_SIZE;
+ return mConfiguredCameraPinBytes;
case KEY_HOME:
return mConfiguredHomePinBytes;
case KEY_ASSISTANT:
- return MAX_ASSISTANT_PIN_SIZE;
+ return mConfiguredAssistantPinBytes;
default:
return 0;
}
@@ -1303,6 +1306,10 @@ public final class PinnerService extends SystemService {
pw.format(" Maximum Pinner quota: %d bytes (%.2f MB)\n", mConfiguredMaxPinnedMemory,
mConfiguredMaxPinnedMemory / bytesPerMB);
pw.format(" Max Home App Pin Bytes (without deps): %d\n", mConfiguredHomePinBytes);
+ pw.format(" Max Assistant App Pin Bytes (without deps): %d\n",
+ mConfiguredAssistantPinBytes);
+ pw.format(
+ " Max Camera App Pin Bytes (without deps): %d\n", mConfiguredCameraPinBytes);
pw.format("\nPinned Files:\n");
synchronized (PinnerService.this) {
long totalSize = 0;
diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
index 49a6ffde6783..a221152222ee 100644
--- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
+++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
@@ -55,6 +55,8 @@ public class BackgroundUserSoundNotifier {
private static final String ACTION_SWITCH_USER = "com.android.server.ACTION_SWITCH_TO_USER";
private static final String ACTION_DISMISS_NOTIFICATION =
"com.android.server.ACTION_DISMISS_NOTIFICATION";
+ private static final String EXTRA_NOTIFICATION_CLIENT_UID =
+ "com.android.server.EXTRA_CLIENT_UID";
/**
* The clientUid from the AudioFocusInfo of the background user,
* for which an active notification is currently displayed.
@@ -101,7 +103,7 @@ public class BackgroundUserSoundNotifier {
ActivityManager am = mSystemUserContext.getSystemService(ActivityManager.class);
registerReceiver(am);
- mBgUserListener = new BackgroundUserListener(mSystemUserContext);
+ mBgUserListener = new BackgroundUserListener();
AudioPolicy.Builder focusControlPolicyBuilder = new AudioPolicy.Builder(mSystemUserContext);
focusControlPolicyBuilder.setLooper(Looper.getMainLooper());
@@ -119,26 +121,16 @@ public class BackgroundUserSoundNotifier {
final class BackgroundUserListener extends AudioPolicy.AudioPolicyFocusListener {
- Context mSystemContext;
-
- BackgroundUserListener(Context systemContext) {
- mSystemContext = systemContext;
- }
-
- @SuppressLint("MissingPermission")
public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {
try {
- BackgroundUserSoundNotifier.this.notifyForegroundUserAboutSoundIfNecessary(afi,
- mSystemContext.createContextAsUser(
- UserHandle.of(ActivityManager.getCurrentUser()), 0));
+ BackgroundUserSoundNotifier.this.notifyForegroundUserAboutSoundIfNecessary(afi);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
- @SuppressLint("MissingPermission")
public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {
- BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary();
+ BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary(afi.getClientUid());
}
}
@@ -160,7 +152,7 @@ public class BackgroundUserSoundNotifier {
if (mNotificationClientUid == -1) {
return;
}
- dismissNotification();
+ dismissNotification(mNotificationClientUid);
if (DEBUG) {
final int actionIndex = intent.getAction().lastIndexOf(".") + 1;
@@ -171,7 +163,7 @@ public class BackgroundUserSoundNotifier {
}
if (ACTION_MUTE_SOUND.equals(intent.getAction())) {
- muteAlarmSounds(mSystemUserContext);
+ muteAlarmSounds(mNotificationClientUid);
} else if (ACTION_SWITCH_USER.equals(intent.getAction())) {
activityManager.switchUser(UserHandle.getUserId(mNotificationClientUid));
}
@@ -193,17 +185,17 @@ public class BackgroundUserSoundNotifier {
*/
@SuppressLint("MissingPermission")
@VisibleForTesting
- void muteAlarmSounds(Context context) {
- AudioManager audioManager = context.getSystemService(AudioManager.class);
+ void muteAlarmSounds(int notificationClientUid) {
+ AudioManager audioManager = mSystemUserContext.getSystemService(AudioManager.class);
if (audioManager != null) {
for (AudioPlaybackConfiguration apc : audioManager.getActivePlaybackConfigurations()) {
- if (apc.getClientUid() == mNotificationClientUid && apc.getPlayerProxy() != null) {
+ if (apc.getClientUid() == notificationClientUid && apc.getPlayerProxy() != null) {
apc.getPlayerProxy().stop();
}
}
}
- AudioFocusInfo currentAfi = getAudioFocusInfoForNotification();
+ AudioFocusInfo currentAfi = getAudioFocusInfoForNotification(notificationClientUid);
if (currentAfi != null) {
mFocusControlAudioPolicy.sendFocusLossAndUpdate(currentAfi);
}
@@ -212,9 +204,14 @@ public class BackgroundUserSoundNotifier {
/**
* Check if sound is coming from background user and show notification is required.
*/
+ @SuppressLint("MissingPermission")
@VisibleForTesting
- void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi, Context foregroundContext)
- throws RemoteException {
+ void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi) throws RemoteException {
+ if (afi == null) {
+ return;
+ }
+ Context foregroundContext = mSystemUserContext.createContextAsUser(
+ UserHandle.of(ActivityManager.getCurrentUser()), 0);
final int userId = UserHandle.getUserId(afi.getClientUid());
final int usage = afi.getAttributes().getUsage();
UserInfo userInfo = mUserManager.getUserInfo(userId);
@@ -232,8 +229,8 @@ public class BackgroundUserSoundNotifier {
mNotificationClientUid = afi.getClientUid();
- mNotificationManager.notifyAsUser(LOG_TAG, mNotificationClientUid,
- createNotification(userInfo.name, foregroundContext),
+ mNotificationManager.notifyAsUser(LOG_TAG, afi.getClientUid(),
+ createNotification(userInfo.name, foregroundContext, afi.getClientUid()),
foregroundContext.getUser());
}
}
@@ -245,14 +242,15 @@ public class BackgroundUserSoundNotifier {
* focus ownership.
*/
@VisibleForTesting
- void dismissNotificationIfNecessary() {
- if (getAudioFocusInfoForNotification() == null && mNotificationClientUid >= 0) {
+ void dismissNotificationIfNecessary(int notificationClientUid) {
+ if (getAudioFocusInfoForNotification(notificationClientUid) == null
+ && mNotificationClientUid >= 0) {
if (DEBUG) {
Log.d(LOG_TAG, "Alarm ringing on background user "
- + UserHandle.getUserHandleForUid(mNotificationClientUid).getIdentifier()
+ + UserHandle.getUserHandleForUid(notificationClientUid).getIdentifier()
+ " left focus stack, dismissing notification");
}
- dismissNotification();
+ dismissNotification(notificationClientUid);
mNotificationClientUid = -1;
}
}
@@ -262,8 +260,8 @@ public class BackgroundUserSoundNotifier {
* shown.
*/
@SuppressLint("MissingPermission")
- private void dismissNotification() {
- mNotificationManager.cancelAsUser(LOG_TAG, mNotificationClientUid, UserHandle.ALL);
+ private void dismissNotification(int notificationClientUid) {
+ mNotificationManager.cancelAsUser(LOG_TAG, notificationClientUid, UserHandle.ALL);
}
/**
@@ -272,11 +270,11 @@ public class BackgroundUserSoundNotifier {
@SuppressLint("MissingPermission")
@VisibleForTesting
@Nullable
- AudioFocusInfo getAudioFocusInfoForNotification() {
- if (mNotificationClientUid >= 0) {
+ AudioFocusInfo getAudioFocusInfoForNotification(int notificationClientUid) {
+ if (notificationClientUid >= 0) {
List<AudioFocusInfo> stack = mFocusControlAudioPolicy.getFocusStack();
for (int i = stack.size() - 1; i >= 0; i--) {
- if (stack.get(i).getClientUid() == mNotificationClientUid) {
+ if (stack.get(i).getClientUid() == notificationClientUid) {
return stack.get(i);
}
}
@@ -284,22 +282,24 @@ public class BackgroundUserSoundNotifier {
return null;
}
- private PendingIntent createPendingIntent(String intentAction) {
+ private PendingIntent createPendingIntent(String intentAction, int notificationClientUid) {
final Intent intent = new Intent(intentAction);
- PendingIntent resultPI = PendingIntent.getBroadcast(mSystemUserContext, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- return resultPI;
+ intent.putExtra(EXTRA_NOTIFICATION_CLIENT_UID, notificationClientUid);
+ return PendingIntent.getBroadcast(mSystemUserContext, notificationClientUid, intent,
+ PendingIntent.FLAG_IMMUTABLE);
}
+ @SuppressLint("MissingPermission")
@VisibleForTesting
- Notification createNotification(String userName, Context fgContext) {
+ Notification createNotification(String userName, Context fgContext, int notificationClientUid) {
final String title = fgContext.getString(R.string.bg_user_sound_notification_title_alarm,
userName);
final int icon = R.drawable.ic_audio_alarm;
- PendingIntent mutePI = createPendingIntent(ACTION_MUTE_SOUND);
- PendingIntent switchPI = createPendingIntent(ACTION_SWITCH_USER);
- PendingIntent dismissNotificationPI = createPendingIntent(ACTION_DISMISS_NOTIFICATION);
+ PendingIntent mutePI = createPendingIntent(ACTION_MUTE_SOUND, notificationClientUid);
+ PendingIntent switchPI = createPendingIntent(ACTION_SWITCH_USER, notificationClientUid);
+ PendingIntent dismissNotificationPI = createPendingIntent(ACTION_DISMISS_NOTIFICATION,
+ notificationClientUid);
final Notification.Action mute = new Notification.Action.Builder(null,
fgContext.getString(R.string.bg_user_sound_notification_button_mute),
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 369029adac59..84a5f2b0e8bc 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -80,11 +80,6 @@ import java.util.function.BiFunction;
*/
public final class BroadcastHelper {
private static final boolean DEBUG_BROADCASTS = false;
- /**
- * Permissions required in order to receive instant application lifecycle broadcasts.
- */
- private static final String[] INSTANT_APP_BROADCAST_PERMISSION =
- new String[]{android.Manifest.permission.ACCESS_INSTANT_APPS};
private final UserManagerInternal mUmInternal;
private final ActivityManagerInternal mAmInternal;
@@ -115,7 +110,7 @@ public final class BroadcastHelper {
SparseArray<int[]> broadcastAllowList = new SparseArray<>();
broadcastAllowList.put(userId, visibilityAllowList);
broadcastIntent(intent, finishedReceiver, isInstantApp, userId, broadcastAllowList,
- filterExtrasForReceiver, bOptions);
+ filterExtrasForReceiver, bOptions, null /* requiredPermissions */);
}
void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
@@ -123,7 +118,7 @@ public final class BroadcastHelper {
final int[] userIds, int[] instantUserIds,
@Nullable SparseArray<int[]> broadcastAllowList,
@Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
- @Nullable Bundle bOptions) {
+ @Nullable Bundle bOptions, @Nullable String[] requiredPermissions) {
try {
final IActivityManager am = ActivityManager.getService();
if (am == null) return;
@@ -137,12 +132,12 @@ public final class BroadcastHelper {
if (ArrayUtils.isEmpty(instantUserIds)) {
doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
resolvedUserIds, false /* isInstantApp */, broadcastAllowList,
- filterExtrasForReceiver, bOptions);
+ filterExtrasForReceiver, bOptions, requiredPermissions);
} else {
// send restricted broadcasts for instant apps
doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
- instantUserIds, true /* isInstantApp */, null,
- null /* filterExtrasForReceiver */, bOptions);
+ instantUserIds, true /* isInstantApp */, null /* broadcastAllowList */,
+ null /* filterExtrasForReceiver */, bOptions, requiredPermissions);
}
} catch (RemoteException ex) {
}
@@ -166,7 +161,8 @@ public final class BroadcastHelper {
boolean isInstantApp,
@Nullable SparseArray<int[]> broadcastAllowList,
@Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
- @Nullable Bundle bOptions) {
+ @Nullable Bundle bOptions,
+ @Nullable String[] requiredPermissions) {
for (int userId : userIds) {
final Intent intent = new Intent(action,
pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
@@ -189,17 +185,18 @@ public final class BroadcastHelper {
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
broadcastIntent(intent, finishedReceiver, isInstantApp, userId, broadcastAllowList,
- filterExtrasForReceiver, bOptions);
+ filterExtrasForReceiver, bOptions, requiredPermissions);
}
}
-
private void broadcastIntent(Intent intent, IIntentReceiver finishedReceiver,
boolean isInstantApp, int userId, @Nullable SparseArray<int[]> broadcastAllowList,
@Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
- @Nullable Bundle bOptions) {
- final String[] requiredPermissions =
- isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null;
+ @Nullable Bundle bOptions, @Nullable String[] requiredPermissions) {
+ if (isInstantApp) {
+ requiredPermissions = ArrayUtils.appendElement(String.class, requiredPermissions,
+ android.Manifest.permission.ACCESS_INSTANT_APPS);
+ }
if (DEBUG_BROADCASTS) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
@@ -234,7 +231,7 @@ public final class BroadcastHelper {
null /* instantUserIds */, null /* broadcastAllowList */,
(callingUid, intentExtras) -> filterExtrasChangedPackageList(
snapshot, callingUid, intentExtras),
- null /* bOptions */);
+ null /* bOptions */, null /* requiredPermissions */);
}
/**
@@ -294,14 +291,29 @@ public final class BroadcastHelper {
return bOptions;
}
- private void sendPackageChangedBroadcast(@NonNull String packageName,
- boolean dontKillApp,
- @NonNull ArrayList<String> componentNames,
- int packageUid,
- @Nullable String reason,
- @Nullable int[] userIds,
- @Nullable int[] instantUserIds,
- @Nullable SparseArray<int[]> broadcastAllowList) {
+ private void sendPackageChangedBroadcastInternal(@NonNull String packageName,
+ boolean dontKillApp,
+ @NonNull ArrayList<String> componentNames,
+ int packageUid,
+ @Nullable String reason,
+ @Nullable int[] userIds,
+ @Nullable int[] instantUserIds,
+ @Nullable SparseArray<int[]> broadcastAllowList) {
+ sendPackageChangedBroadcastWithPermissions(packageName, dontKillApp, componentNames,
+ packageUid, reason, userIds, instantUserIds, broadcastAllowList,
+ null /* targetPackageName */, null /* requiredPermissions */);
+ }
+
+ private void sendPackageChangedBroadcastWithPermissions(@NonNull String packageName,
+ boolean dontKillApp,
+ @NonNull ArrayList<String> componentNames,
+ int packageUid,
+ @Nullable String reason,
+ @Nullable int[] userIds,
+ @Nullable int[] instantUserIds,
+ @Nullable SparseArray<int[]> broadcastAllowList,
+ @Nullable String targetPackageName,
+ @Nullable String[] requiredPermissions) {
if (DEBUG_INSTALL) {
Log.v(TAG, "Sending package changed: package=" + packageName + " components="
+ componentNames);
@@ -321,9 +333,10 @@ public final class BroadcastHelper {
// little component state change.
final int flags = !componentNames.contains(packageName)
? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0;
- sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null,
- userIds, instantUserIds, broadcastAllowList, null /* filterExtrasForReceiver */,
- null /* bOptions */);
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags,
+ targetPackageName, null /* finishedReceiver */, userIds, instantUserIds,
+ broadcastAllowList, null /* filterExtrasForReceiver */, null /* bOptions */,
+ requiredPermissions);
}
static void sendDeviceCustomizationReadyBroadcast() {
@@ -680,7 +693,8 @@ public final class BroadcastHelper {
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
packageName, extras, 0, null, null, userIds, instantUserIds,
- broadcastAllowlist, null /* filterExtrasForReceiver */, null);
+ broadcastAllowlist, null /* filterExtrasForReceiver */, null /* bOptions */,
+ null /* requiredPermissions */);
// Send to PermissionController for all new users, even if it may not be running for some
// users
if (isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) {
@@ -688,7 +702,8 @@ public final class BroadcastHelper {
packageName, extras, 0,
mContext.getPackageManager().getPermissionControllerPackageName(),
null, userIds, instantUserIds,
- broadcastAllowlist, null /* filterExtrasForReceiver */, null);
+ broadcastAllowlist, null /* filterExtrasForReceiver */, null /* bOptions */,
+ null /* requiredPermissions */);
}
}
@@ -719,7 +734,8 @@ public final class BroadcastHelper {
int[] userIds, int[] instantUserIds) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */,
- null /* filterExtrasForReceiver */, null);
+ null /* filterExtrasForReceiver */, null /* bOptions */,
+ null /* requiredPermissions */);
}
/**
@@ -824,7 +840,7 @@ public final class BroadcastHelper {
final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
final SparseArray<int[]> broadcastAllowList =
isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
- mHandler.post(() -> sendPackageChangedBroadcast(
+ mHandler.post(() -> sendPackageChangedBroadcastInternal(
packageName, dontKillApp, componentNames, packageUid, reason, userIds,
instantUserIds, broadcastAllowList));
mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
@@ -843,7 +859,7 @@ public final class BroadcastHelper {
@Nullable Bundle bOptions) {
mHandler.post(() -> sendPackageBroadcast(action, pkg, extras, flags,
targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList,
- null /* filterExtrasForReceiver */, bOptions));
+ null /* filterExtrasForReceiver */, bOptions, null /* requiredPermissions */));
if (targetPkg == null) {
// For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called
// many times to different targets, e.g. installer app, permission controller, other
@@ -1014,7 +1030,7 @@ public final class BroadcastHelper {
extras, flags, null /* targetPkg */, null /* finishedReceiver */,
new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
filterExtrasForReceiver,
- options));
+ options, null /* requiredPermissions */));
notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId},
null /* instantUserIds */, null /* broadcastAllowList */, filterExtrasForReceiver);
}
@@ -1046,9 +1062,12 @@ public final class BroadcastHelper {
} else {
intentExtras = null;
}
- doSendBroadcast(action, null, intentExtras,
- Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
- targetUserIds, false, null, null, null);
+ doSendBroadcast(action, null /* pkg */, intentExtras,
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName,
+ null /* finishedReceiver */,
+ targetUserIds, false /* isInstantApp */, null /* broadcastAllowList */,
+ null /* filterExtrasForReceiver */, null /* bOptions */,
+ null /* requiredPermissions */);
}
});
}
@@ -1077,7 +1096,7 @@ public final class BroadcastHelper {
null /* broadcastAllowList */,
(callingUid, intentExtras) -> filterExtrasChangedPackageList(
snapshot, callingUid, intentExtras),
- null /* bOptions */));
+ null /* bOptions */, null /* requiredPermissions */));
}
void sendResourcesChangedBroadcastAndNotify(@NonNull Computer snapshot,
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 9e8598a04a98..0b58c759b284 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -791,16 +791,6 @@ public final class DexOptHelper {
}
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
-
- // This mirrors logic from commitReconciledScanResultLocked, where the library
- // files needed for dexopt are assigned.
- PackageSetting realPkgSetting = installRequest.getRealPackageSetting();
- // Unfortunately, the updated system app flag is only tracked on this
- // PackageSetting
- boolean isUpdatedSystemApp =
- installRequest.getScannedPackageSetting().isUpdatedSystemApp();
- realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
-
DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService(
installRequest, dexoptOptions);
installRequest.onDexoptFinished(dexOptResult);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 34d939b07187..f6a808b6c33e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -25,12 +25,14 @@ import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVI
import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED;
import static android.content.pm.PackageInstaller.UNARCHIVAL_GENERIC_ERROR;
import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_WARN;
import static android.content.pm.PackageManager.DELETE_ARCHIVE;
import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
import static com.android.server.pm.PackageArchiver.isArchivingEnabled;
+import static com.android.server.pm.PackageInstallerSession.isValidVerificationPolicy;
import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -150,6 +152,7 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntPredicate;
import java.util.function.Supplier;
@@ -275,6 +278,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
};
+ /**
+ * Default verification policy for incoming installation sessions.
+ * TODO(b/360129657): update the default policy.
+ */
+ private final AtomicInteger mVerificationPolicy = new AtomicInteger(
+ VERIFICATION_POLICY_BLOCK_FAIL_WARN);
+
private static final class Lifecycle extends SystemService {
private final PackageInstallerService mPackageInstallerService;
@@ -1042,7 +1052,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
false, false, false, PackageManager.INSTALL_UNKNOWN, "", null,
- mVerifierController);
+ mVerifierController, mVerificationPolicy.get());
synchronized (mSessions) {
mSessions.put(sessionId, session);
@@ -1866,6 +1876,34 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
}
+ @Override
+ public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("You need the "
+ + "com.android.permission.VERIFICATION_AGENT permission "
+ + "to get the verification policy");
+ }
+ return mVerificationPolicy.get();
+ }
+
+ @Override
+ public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("You need the "
+ + "com.android.permission.VERIFICATION_AGENT permission "
+ + "to set the verification policy");
+ }
+ if (!isValidVerificationPolicy(policy)) {
+ return false;
+ }
+ if (policy != mVerificationPolicy.get()) {
+ mVerificationPolicy.set(policy);
+ }
+ return true;
+ }
+
private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
int installerUid) {
int count = 0;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 9e0ba8492ab9..04d0d182d3b2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -21,9 +21,17 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_INSTA
import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_UPDATED_BY_DO;
import static android.content.pm.DataLoaderType.INCREMENTAL;
import static android.content.pm.DataLoaderType.STREAMING;
+import static android.content.pm.PackageInstaller.EXTRA_VERIFICATION_FAILURE_REASON;
import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
import static android.content.pm.PackageInstaller.UNARCHIVAL_STATUS_UNSET;
+import static android.content.pm.PackageInstaller.VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE;
+import static android.content.pm.PackageInstaller.VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED;
+import static android.content.pm.PackageInstaller.VERIFICATION_FAILED_REASON_UNKNOWN;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_OPEN;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_WARN;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_NONE;
import static android.content.pm.PackageItemInfo.MAX_SAFE_LABEL_LENGTH;
import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE;
@@ -38,7 +46,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_VERIFICATION_FAIL
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
-import static android.content.pm.verify.pkg.VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN;
+import static android.content.pm.verify.pkg.VerificationSession.VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE;
import static android.os.Process.INVALID_UID;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
import static android.system.OsConstants.O_CREAT;
@@ -313,6 +321,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final String ATTR_APPLICATION_ENABLED_SETTING_PERSISTENT =
"applicationEnabledSettingPersistent";
private static final String ATTR_DOMAIN = "domain";
+ private static final String ATTR_VERIFICATION_POLICY = "verificationPolicy";
private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT;
@@ -410,6 +419,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private final PackageSessionProvider mSessionProvider;
private final SilentUpdatePolicy mSilentUpdatePolicy;
/**
+ * The verification policy applied to this session, which might be different from the default
+ * verification policy used by the system.
+ */
+ private final AtomicInteger mVerificationPolicy;
+ /**
* Note all calls must be done outside {@link #mLock} to prevent lock inversion.
*/
private final StagingManager mStagingManager;
@@ -791,7 +805,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (errorMsg != null) {
Slog.e(TAG, "verifySession error: " + errorMsg);
setSessionFailed(INSTALL_FAILED_INTERNAL_ERROR, errorMsg);
- onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, errorMsg);
+ onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, errorMsg,
+ /* extras= */ null);
return false;
}
return true;
@@ -1167,7 +1182,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
boolean isFailed, boolean isApplied, int sessionErrorCode,
String sessionErrorMessage, DomainSet preVerifiedDomains,
- @NonNull VerifierController verifierController) {
+ @NonNull VerifierController verifierController,
+ @PackageInstaller.VerificationPolicy int verificationPolicy) {
mCallback = callback;
mContext = context;
mPm = pm;
@@ -1177,6 +1193,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mHandler = new Handler(looper, mHandlerCallback);
mStagingManager = stagingManager;
mVerifierController = verifierController;
+ mVerificationPolicy = new AtomicInteger(verificationPolicy);
this.sessionId = sessionId;
this.userId = userId;
@@ -2580,10 +2597,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
dispatchSessionFinished(error, detailMessage, null);
}
- private void onSessionVerificationFailure(int error, String msg) {
+ private void onSessionVerificationFailure(int error, String msg, Bundle extras) {
Slog.e(TAG, "Failed to verify session " + sessionId);
// Dispatch message to remove session from PackageInstallerService.
- dispatchSessionFinished(error, msg, null);
+ dispatchSessionFinished(error, msg, extras);
maybeFinishChildSessions(error, msg);
}
@@ -2856,7 +2873,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
setSessionFailed(e.error, errorMsg);
- onSessionVerificationFailure(e.error, errorMsg);
+ onSessionVerificationFailure(e.error, errorMsg, /* extras= */ null);
}
if (Flags.verificationService()) {
final Supplier<Computer> snapshotSupplier = mPm::snapshotComputer;
@@ -2872,11 +2889,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// the installation can proceed.
if (!mVerifierController.startVerificationSession(snapshotSupplier, userId,
sessionId, getPackageName(), Uri.fromFile(stageDir), signingInfo,
- declaredLibraries, /* extensionParams= */ null,
+ declaredLibraries, mVerificationPolicy.get(), /* extensionParams= */ null,
new VerifierCallback(), /* retry= */ false)) {
// A verifier is installed but cannot be connected. Installation disallowed.
onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR,
- "A verifier agent is available on device but cannot be connected.");
+ "A verifier agent is available on device but cannot be connected.",
+ /* extras= */ null);
}
} else {
// Verifier is not installed. Let the installation pass for now.
@@ -2917,7 +2935,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
setSessionFailed(e.error, errorMsg);
- onSessionVerificationFailure(e.error, errorMsg);
+ onSessionVerificationFailure(e.error, errorMsg, /* extras= */ null);
}
}
@@ -2926,24 +2944,57 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
*/
public class VerifierCallback {
/**
+ * Called by the VerifierController when the verifier requests to get the current
+ * verification policy for this session.
+ */
+ public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
+ return mVerificationPolicy.get();
+ }
+ /**
+ * Called by the VerifierController when the verifier requests to change the verification
+ * policy for this session.
+ */
+ public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
+ if (!isValidVerificationPolicy(policy)) {
+ return false;
+ }
+ mVerificationPolicy.set(policy);
+ return true;
+ }
+ /**
* Called by the VerifierController when the connection has failed.
*/
public void onConnectionFailed() {
- mHandler.post(() -> {
- onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
- "A verifier agent is available on device but cannot be connected.");
- });
+ // TODO(b/360129657): prompt user on fail warning
+ handleNonPackageBlockedFailure(
+ /* onFailWarning= */ PackageInstallerSession.this::resumeVerify,
+ /* onFailClosed= */ () -> {
+ Bundle bundle = new Bundle();
+ bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON,
+ VERIFICATION_FAILED_REASON_UNKNOWN);
+ onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
+ "A verifier agent is available on device but cannot be connected.",
+ bundle);
+
+ });
}
/**
* Called by the VerifierController when the verification request has timed out.
*/
public void onTimeout() {
- mHandler.post(() -> {
- mVerifierController.notifyVerificationTimeout(sessionId);
- onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
- "Verification timed out; missing a response from the verifier within the"
- + " time limit");
- });
+ // Always notify the verifier, regardless of the policy.
+ mVerifierController.notifyVerificationTimeout(sessionId);
+ // TODO(b/360129657): prompt user on fail warning
+ handleNonPackageBlockedFailure(
+ /* onFailWarning= */ PackageInstallerSession.this::resumeVerify,
+ /* onFailClosed= */ () -> {
+ Bundle bundle = new Bundle();
+ bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON,
+ VERIFICATION_FAILED_REASON_UNKNOWN);
+ onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
+ "Verification timed out; missing a response from the verifier"
+ + " within the time limit", bundle);
+ });
}
/**
* Called by the VerifierController when the verification request has received a complete
@@ -2953,17 +3004,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Nullable PersistableBundle extensionResponse) {
// TODO: handle extension response
mHandler.post(() -> {
- if (statusReceived.isVerified()) {
+ if (statusReceived.isVerified()
+ || mVerificationPolicy.get() == VERIFICATION_POLICY_NONE) {
// Continue with the rest of the verification and installation.
resumeVerify();
- } else {
- StringBuilder sb = new StringBuilder("Verifier rejected the installation");
- if (!TextUtils.isEmpty(statusReceived.getFailureMessage())) {
- sb.append(" with message: ").append(statusReceived.getFailureMessage());
- }
- onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
- sb.toString());
+ return;
+ }
+ // Package is blocked.
+ StringBuilder sb = new StringBuilder("Verifier rejected the installation");
+ if (!TextUtils.isEmpty(statusReceived.getFailureMessage())) {
+ sb.append(" with message: ").append(statusReceived.getFailureMessage());
}
+ Bundle bundle = new Bundle();
+ bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON,
+ VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED);
+ onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
+ sb.toString(), bundle);
});
}
/**
@@ -2971,16 +3027,51 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* response.
*/
public void onVerificationIncompleteReceived(int incompleteReason) {
- mHandler.post(() -> {
- if (incompleteReason == VERIFICATION_INCOMPLETE_UNKNOWN) {
- // TODO: change this to a user confirmation and handle other incomplete reasons
- onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR,
- "Verification cannot be completed for unknown reasons.");
+ // TODO(b/360129657): prompt user on fail warning
+ handleNonPackageBlockedFailure(
+ /* onFailWarning= */ PackageInstallerSession.this::resumeVerify,
+ /* onFailClosed= */ () -> {
+ final int failureReason;
+ StringBuilder sb = new StringBuilder(
+ "Verification cannot be completed because of ");
+ if (incompleteReason == VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE) {
+ failureReason = VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE;
+ sb.append("unavailable network.");
+ } else {
+ failureReason = VERIFICATION_FAILED_REASON_UNKNOWN;
+ sb.append("unknown reasons.");
+ }
+ Bundle bundle = new Bundle();
+ bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON, failureReason);
+ onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
+ sb.toString(), bundle);
+ });
+ }
+
+ private void handleNonPackageBlockedFailure(Runnable onFailWarning, Runnable onFailClosed) {
+ final Runnable r = switch (mVerificationPolicy.get()) {
+ case VERIFICATION_POLICY_NONE, VERIFICATION_POLICY_BLOCK_FAIL_OPEN ->
+ PackageInstallerSession.this::resumeVerify;
+ case VERIFICATION_POLICY_BLOCK_FAIL_WARN -> onFailWarning;
+ case VERIFICATION_POLICY_BLOCK_FAIL_CLOSED -> onFailClosed;
+ default -> {
+ Log.wtf(TAG, "Unknown verification policy: " + mVerificationPolicy.get());
+ yield onFailClosed;
}
- });
+ };
+ mHandler.post(r);
}
}
+ /**
+ * Returns whether a policy is a valid verification policy.
+ */
+ public static boolean isValidVerificationPolicy(
+ @PackageInstaller.VerificationPolicy int policy) {
+ return policy >= VERIFICATION_POLICY_NONE
+ && policy <= VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
+ }
+
private IntentSender getRemoteStatusReceiver() {
synchronized (mLock) {
return mRemoteStatusReceiver;
@@ -3156,7 +3247,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (error == INSTALL_SUCCEEDED) {
onVerificationComplete();
} else {
- onSessionVerificationFailure(error, msg);
+ onSessionVerificationFailure(error, msg, /* extras= */ null);
}
});
});
@@ -5328,6 +5419,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
+ /**
+ * @return the current policy for the verification request associated with this session.
+ */
+ @VisibleForTesting
+ public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
+ assertCallerIsOwnerOrRoot();
+ return mVerificationPolicy.get();
+ }
void setSessionReady() {
synchronized (mLock) {
@@ -5631,6 +5730,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (!ArrayUtils.isEmpty(warnings)) {
fillIn.putStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS, warnings);
}
+ if (extras.containsKey(EXTRA_VERIFICATION_FAILURE_REASON)) {
+ fillIn.putExtra(EXTRA_VERIFICATION_FAILURE_REASON,
+ extras.getInt(EXTRA_VERIFICATION_FAILURE_REASON));
+ }
}
try {
final BroadcastOptions options = BroadcastOptions.makeBasic();
@@ -5786,6 +5889,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
out.attributeInt(null, ATTR_INSTALL_REASON, params.installReason);
writeBooleanAttribute(out, ATTR_APPLICATION_ENABLED_SETTING_PERSISTENT,
params.applicationEnabledSettingPersistent);
+ out.attributeInt(null, ATTR_VERIFICATION_POLICY, mVerificationPolicy.get());
final boolean isDataLoader = params.dataLoaderParams != null;
writeBooleanAttribute(out, ATTR_IS_DATALOADER, isDataLoader);
@@ -5936,6 +6040,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final boolean sealed = in.getAttributeBoolean(null, ATTR_SEALED, false);
final int parentSessionId = in.getAttributeInt(null, ATTR_PARENT_SESSION_ID,
SessionInfo.INVALID_ID);
+ final int verificationPolicy = in.getAttributeInt(null, ATTR_VERIFICATION_POLICY,
+ VERIFICATION_POLICY_NONE);
final SessionParams params = new SessionParams(
SessionParams.MODE_INVALID);
@@ -6110,6 +6216,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
installerUid, installSource, params, createdMillis, committedMillis, stageDir,
stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed,
childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied,
- sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController);
+ sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController,
+ verificationPolicy);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 455776993c56..d78f12217271 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4709,10 +4709,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService
extras.putLong(Intent.EXTRA_TIME, SystemClock.elapsedRealtime());
mHandler.post(() -> {
mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_UNSTOPPED,
- packageName, extras,
- Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
- userIds, null, broadcastAllowList, null,
- null);
+ packageName, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY,
+ null /* targetPkg */, null /* finishedReceiver */, userIds,
+ null /* instantUserIds */, broadcastAllowList,
+ null /* filterExtrasForReceiver */, null /* bOptions */,
+ null /* requiredPermissions */);
});
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_UNSTOPPED,
packageName, extras, userIds, null /* instantUserIds */,
@@ -7169,17 +7170,17 @@ public class PackageManagerService implements PackageSender, TestUtilityService
// Sent async using the PM handler, to maintain ordering with PACKAGE_UNSTOPPED
mHandler.post(() -> {
mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED,
- packageName, extras,
- flags, null, null,
- userIds, null, broadcastAllowList, null,
- null);
+ packageName, extras, flags, null /* targetPkg */,
+ null /* finishedReceiver */, userIds, null /* instantUserIds */,
+ broadcastAllowList, null /* filterExtrasForReceiver */,
+ null /* bOptions */, null /* requiredPermissions */);
});
} else {
mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED,
- packageName, extras,
- flags, null, null,
- userIds, null, broadcastAllowList, null,
- null);
+ packageName, extras, flags, null /* targetPkg */,
+ null /* finishedReceiver */, userIds, null /* instantUserIds */,
+ broadcastAllowList, null /* filterExtrasForReceiver */, null /* bOptions */,
+ null /* requiredPermissions */);
}
mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_RESTARTED,
packageName, extras, userIds, null /* instantUserIds */,
diff --git a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
index 7eac940933c2..b7cc7ccead89 100644
--- a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
+++ b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
@@ -30,6 +30,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.SharedLibraryInfo;
@@ -94,9 +95,22 @@ public class VerifierController {
// Max duration allowed to wait for a verifier to respond to a verification request.
private static final long DEFAULT_MAX_VERIFICATION_REQUEST_EXTENDED_TIMEOUT_MILLIS =
TimeUnit.MINUTES.toMillis(10);
+ /**
+ * Configurable maximum amount of time in milliseconds for the system to wait from the moment
+ * when the installation session requires a verification, till when the request is delivered to
+ * the verifier, pending the connection to be established. If the request has not been delivered
+ * to the verifier within this amount of time, e.g., because the verifier has crashed or ANR'd,
+ * the controller then sends a failure status back to the installation session.
+ * Flag type: {@code long}
+ * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE
+ */
+ private static final String PROPERTY_VERIFIER_CONNECTION_TIMEOUT_MILLIS =
+ "verifier_connection_timeout_millis";
// The maximum amount of time to wait from the moment when the session requires a verification,
// till when the request is delivered to the verifier, pending the connection to be established.
- private static final long CONNECTION_TIMEOUT_SECONDS = 10;
+ private static final long DEFAULT_VERIFIER_CONNECTION_TIMEOUT_MILLIS =
+ TimeUnit.SECONDS.toMillis(10);
+
// The maximum amount of time to wait before the system unbinds from the verifier.
private static final long UNBIND_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(6);
@@ -271,6 +285,7 @@ public class VerifierController {
int installationSessionId, String packageName,
Uri stagedPackageUri, SigningInfo signingInfo,
List<SharedLibraryInfo> declaredLibraries,
+ @PackageInstaller.VerificationPolicy int verificationPolicy,
PersistableBundle extensionParams, PackageInstallerSession.VerifierCallback callback,
boolean retry) {
// Try connecting to the verifier if not already connected
@@ -292,7 +307,7 @@ public class VerifierController {
/* id= */ verificationId,
/* installSessionId= */ installationSessionId,
packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams,
- new VerificationSessionInterface(),
+ verificationPolicy, new VerificationSessionInterface(callback),
new VerificationSessionCallback(callback));
AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
if (!retry) {
@@ -306,7 +321,8 @@ public class VerifierController {
}
service.onVerificationRetry(session);
}
- }).orTimeout(CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS).whenComplete((res, err) -> {
+ }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
+ .whenComplete((res, err) -> {
if (err != null) {
Slog.e(TAG, "Error notifying verification request for session " + verificationId,
err);
@@ -407,6 +423,12 @@ public class VerifierController {
// This class handles requests from the remote verifier
private class VerificationSessionInterface extends IVerificationSessionInterface.Stub {
+ private final PackageInstallerSession.VerifierCallback mCallback;
+
+ VerificationSessionInterface(PackageInstallerSession.VerifierCallback callback) {
+ mCallback = callback;
+ }
+
@Override
public long getTimeoutTime(int verificationId) {
checkCallerPermission();
@@ -432,6 +454,20 @@ public class VerifierController {
return tracker.extendTimeRemaining(additionalMs);
}
}
+
+ @Override
+ public boolean setVerificationPolicy(int verificationId,
+ @PackageInstaller.VerificationPolicy int policy) {
+ checkCallerPermission();
+ synchronized (mVerificationStatus) {
+ final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
+ if (tracker == null) {
+ throw new IllegalStateException("Verification session " + verificationId
+ + " doesn't exist or has finished");
+ }
+ }
+ return mCallback.setVerificationPolicy(policy);
+ }
}
private class VerificationSessionCallback extends IVerificationSessionCallback.Stub {
@@ -451,8 +487,8 @@ public class VerifierController {
throw new IllegalStateException("Verification session " + id
+ " doesn't exist or has finished");
}
- mCallback.onVerificationIncompleteReceived(reason);
}
+ mCallback.onVerificationIncompleteReceived(reason);
// Remove status tracking and stop the timeout countdown
removeStatusTracker(id);
}
@@ -630,6 +666,14 @@ public class VerifierController {
return getMaxVerificationExtendedTimeoutMillisFromDeviceConfig();
}
+ /**
+ * This is added so that we can mock the maximum connection timeout duration without
+ * calling into DeviceConfig.
+ */
+ public long getVerifierConnectionTimeoutMillis() {
+ return getVerifierConnectionTimeoutMillisFromDeviceConfig();
+ }
+
private static long getVerificationRequestTimeoutMillisFromDeviceConfig() {
return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
PROPERTY_VERIFICATION_REQUEST_TIMEOUT_MILLIS,
@@ -641,5 +685,11 @@ public class VerifierController {
PROPERTY_MAX_VERIFICATION_REQUEST_EXTENDED_TIMEOUT_MILLIS,
DEFAULT_MAX_VERIFICATION_REQUEST_EXTENDED_TIMEOUT_MILLIS);
}
+
+ private static long getVerifierConnectionTimeoutMillisFromDeviceConfig() {
+ return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
+ PROPERTY_VERIFIER_CONNECTION_TIMEOUT_MILLIS,
+ DEFAULT_VERIFIER_CONNECTION_TIMEOUT_MILLIS);
+ }
}
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index eb62b5631c43..8ba56c5320f2 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -771,6 +771,10 @@ public class Notifier {
public void onGroupRemoved(int groupId) {
mInteractivityByGroupId.remove(groupId);
mWakefulnessSessionObserver.removePowerGroup(groupId);
+ if (mFlags.isPerDisplayWakeByTouchEnabled()) {
+ resetDisplayInteractivities();
+ mInputManagerInternal.setDisplayInteractivities(mDisplayInteractivities);
+ }
}
/**
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index 8f603fc34b32..075a31f3b24c 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
@@ -191,8 +191,7 @@ public class RotationResolverManagerService extends
SensorPrivacyManager.Sensors.CAMERA);
if (mIsServiceEnabled && isCameraAvailable) {
final RotationResolverManagerPerUserService service =
- getServiceForUserLocked(
- UserHandle.getCallingUserId());
+ getServiceForUserLocked(UserHandle.USER_CURRENT);
final RotationResolutionRequest request;
if (packageName == null) {
request = new RotationResolutionRequest(/* packageName */ "",
diff --git a/services/core/java/com/android/server/security/forensic/ForensicService.java b/services/core/java/com/android/server/security/forensic/ForensicService.java
new file mode 100644
index 000000000000..07639d1a3945
--- /dev/null
+++ b/services/core/java/com/android/server/security/forensic/ForensicService.java
@@ -0,0 +1,294 @@
+/*
+ * 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.security.forensic;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.security.forensic.IForensicService;
+import android.security.forensic.IForensicServiceCommandCallback;
+import android.security.forensic.IForensicServiceStateCallback;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ServiceThread;
+import com.android.server.SystemService;
+
+import java.util.ArrayList;
+
+/**
+ * @hide
+ */
+public class ForensicService extends SystemService {
+ private static final String TAG = "ForensicService";
+
+ private static final int MSG_MONITOR_STATE = 0;
+ private static final int MSG_MAKE_VISIBLE = 1;
+ private static final int MSG_MAKE_INVISIBLE = 2;
+ private static final int MSG_ENABLE = 3;
+ private static final int MSG_DISABLE = 4;
+ private static final int MSG_BACKUP = 5;
+
+ private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
+ private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE;
+ private static final int STATE_VISIBLE = IForensicServiceStateCallback.State.VISIBLE;
+ private static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED;
+
+ private static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN;
+ private static final int ERROR_PERMISSION_DENIED =
+ IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED;
+ private static final int ERROR_INVALID_STATE_TRANSITION =
+ IForensicServiceCommandCallback.ErrorCode.INVALID_STATE_TRANSITION;
+ private static final int ERROR_BACKUP_TRANSPORT_UNAVAILABLE =
+ IForensicServiceCommandCallback.ErrorCode.BACKUP_TRANSPORT_UNAVAILABLE;
+ private static final int ERROR_DATA_SOURCE_UNAVAILABLE =
+ IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final BinderService mBinderService;
+
+ private final ArrayList<IForensicServiceStateCallback> mStateMonitors = new ArrayList<>();
+ private volatile int mState = STATE_INVISIBLE;
+
+ public ForensicService(@NonNull Context context) {
+ this(new InjectorImpl(context));
+ }
+
+ @VisibleForTesting
+ ForensicService(@NonNull Injector injector) {
+ super(injector.getContext());
+ mContext = injector.getContext();
+ mHandler = new EventHandler(injector.getLooper(), this);
+ mBinderService = new BinderService(this);
+ }
+
+ @VisibleForTesting
+ protected void setState(int state) {
+ mState = state;
+ }
+
+ private static final class BinderService extends IForensicService.Stub {
+ final ForensicService mService;
+
+ BinderService(ForensicService service) {
+ mService = service;
+ }
+
+ @Override
+ public void monitorState(IForensicServiceStateCallback callback) {
+ mService.mHandler.obtainMessage(MSG_MONITOR_STATE, callback).sendToTarget();
+ }
+
+ @Override
+ public void makeVisible(IForensicServiceCommandCallback callback) {
+ mService.mHandler.obtainMessage(MSG_MAKE_VISIBLE, callback).sendToTarget();
+ }
+
+ @Override
+ public void makeInvisible(IForensicServiceCommandCallback callback) {
+ mService.mHandler.obtainMessage(MSG_MAKE_INVISIBLE, callback).sendToTarget();
+ }
+
+ @Override
+ public void enable(IForensicServiceCommandCallback callback) {
+ mService.mHandler.obtainMessage(MSG_ENABLE, callback).sendToTarget();
+ }
+
+ @Override
+ public void disable(IForensicServiceCommandCallback callback) {
+ mService.mHandler.obtainMessage(MSG_DISABLE, callback).sendToTarget();
+ }
+ }
+
+ private static class EventHandler extends Handler {
+ private final ForensicService mService;
+
+ EventHandler(Looper looper, ForensicService service) {
+ super(looper);
+ mService = service;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_MONITOR_STATE:
+ try {
+ mService.monitorState(
+ (IForensicServiceStateCallback) msg.obj);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException", e);
+ }
+ break;
+ case MSG_MAKE_VISIBLE:
+ try {
+ mService.makeVisible((IForensicServiceCommandCallback) msg.obj);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException", e);
+ }
+ break;
+ case MSG_MAKE_INVISIBLE:
+ try {
+ mService.makeInvisible((IForensicServiceCommandCallback) msg.obj);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException", e);
+ }
+ break;
+ case MSG_ENABLE:
+ try {
+ mService.enable((IForensicServiceCommandCallback) msg.obj);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException", e);
+ }
+ break;
+ case MSG_DISABLE:
+ try {
+ mService.disable((IForensicServiceCommandCallback) msg.obj);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException", e);
+ }
+ break;
+ default:
+ Slog.w(TAG, "Unknown message: " + msg.what);
+ }
+ }
+ }
+
+ private void monitorState(IForensicServiceStateCallback callback) throws RemoteException {
+ for (int i = 0; i < mStateMonitors.size(); i++) {
+ if (mStateMonitors.get(i).asBinder() == callback.asBinder()) {
+ return;
+ }
+ }
+ mStateMonitors.add(callback);
+ callback.onStateChange(mState);
+ }
+
+ private void notifyStateMonitors() throws RemoteException {
+ for (int i = 0; i < mStateMonitors.size(); i++) {
+ mStateMonitors.get(i).onStateChange(mState);
+ }
+ }
+
+ private void makeVisible(IForensicServiceCommandCallback callback) throws RemoteException {
+ switch (mState) {
+ case STATE_INVISIBLE:
+ mState = STATE_VISIBLE;
+ notifyStateMonitors();
+ callback.onSuccess();
+ break;
+ case STATE_VISIBLE:
+ callback.onSuccess();
+ break;
+ default:
+ callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+ }
+ }
+
+ private void makeInvisible(IForensicServiceCommandCallback callback) throws RemoteException {
+ switch (mState) {
+ case STATE_VISIBLE:
+ case STATE_ENABLED:
+ mState = STATE_INVISIBLE;
+ notifyStateMonitors();
+ callback.onSuccess();
+ break;
+ case STATE_INVISIBLE:
+ callback.onSuccess();
+ break;
+ default:
+ callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+ }
+ }
+
+ private void enable(IForensicServiceCommandCallback callback) throws RemoteException {
+ switch (mState) {
+ case STATE_VISIBLE:
+ mState = STATE_ENABLED;
+ notifyStateMonitors();
+ callback.onSuccess();
+ break;
+ case STATE_ENABLED:
+ callback.onSuccess();
+ break;
+ default:
+ callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+ }
+ }
+
+ private void disable(IForensicServiceCommandCallback callback) throws RemoteException {
+ switch (mState) {
+ case STATE_ENABLED:
+ mState = STATE_VISIBLE;
+ notifyStateMonitors();
+ callback.onSuccess();
+ break;
+ case STATE_VISIBLE:
+ callback.onSuccess();
+ break;
+ default:
+ callback.onFailure(ERROR_INVALID_STATE_TRANSITION);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ try {
+ publishBinderService(Context.FORENSIC_SERVICE, mBinderService);
+ } catch (Throwable t) {
+ Slog.e(TAG, "Could not start the ForensicService.", t);
+ }
+ }
+
+ @VisibleForTesting
+ IForensicService getBinderService() {
+ return mBinderService;
+ }
+
+ interface Injector {
+ Context getContext();
+
+ Looper getLooper();
+ }
+
+ private static final class InjectorImpl implements Injector {
+ private final Context mContext;
+
+ InjectorImpl(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+
+ @Override
+ public Looper getLooper() {
+ ServiceThread serviceThread =
+ new ServiceThread(
+ TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
+ serviceThread.start();
+ return serviceThread.getLooper();
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 91a17a9e1c31..4589d26261dc 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -381,10 +381,12 @@ public final class TvInputManagerService extends SystemService {
// service to populate the hardware list.
serviceState = new ServiceState(component, userId);
userState.serviceStateMap.put(component, serviceState);
- updateServiceConnectionLocked(component, userId);
} else {
inputList.addAll(serviceState.hardwareInputMap.values());
}
+ if (serviceState.needInit) {
+ updateServiceConnectionLocked(component, userId);
+ }
} else {
try {
TvInputInfo info = new TvInputInfo.Builder(mContext, ri).build();
@@ -489,6 +491,27 @@ public final class TvInputManagerService extends SystemService {
}
}
+ @GuardedBy("mLock")
+ private void cleanUpHdmiDevices(int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "cleanUpHdmiDevices: user " + userId);
+ }
+ UserState userState = getOrCreateUserStateLocked(userId);
+ for (ServiceState serviceState : userState.serviceStateMap.values()) {
+ for (HdmiDeviceInfo device : mTvInputHardwareManager.getHdmiDeviceList()) {
+ try {
+ if (serviceState.service != null) {
+ serviceState.service.notifyHdmiDeviceRemoved(device);
+ } else {
+ serviceState.hdmiDeviceRemovedBuffer.add(device);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
+ }
+ }
+ }
+ }
+
private void startUser(int userId) {
synchronized (mLock) {
if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) {
@@ -500,9 +523,13 @@ public final class TvInputManagerService extends SystemService {
if (userInfo.isProfile()
&& parentInfo != null
&& parentInfo.id == mCurrentUserId) {
- // only the children of the current user can be started in background
+ int prevUserId = mCurrentUserId;
mCurrentUserId = userId;
- startProfileLocked(userId);
+ // only the children of the current user can be started in background
+ releaseSessionOfUserLocked(prevUserId);
+ cleanUpHdmiDevices(prevUserId);
+ unbindServiceOfUserLocked(prevUserId);
+ startProfileLocked(mCurrentUserId);
}
}
}
@@ -515,6 +542,7 @@ public final class TvInputManagerService extends SystemService {
}
releaseSessionOfUserLocked(userId);
+ cleanUpHdmiDevices(userId);
unbindServiceOfUserLocked(userId);
mRunningProfiles.remove(userId);
}
@@ -543,15 +571,19 @@ public final class TvInputManagerService extends SystemService {
unbindServiceOfUserLocked(runningId);
}
mRunningProfiles.clear();
- releaseSessionOfUserLocked(mCurrentUserId);
- unbindServiceOfUserLocked(mCurrentUserId);
+ int prevUserId = mCurrentUserId;
mCurrentUserId = userId;
- buildTvInputListLocked(userId, null);
- buildTvContentRatingSystemListLocked(userId);
+
+ releaseSessionOfUserLocked(prevUserId);
+ cleanUpHdmiDevices(prevUserId);
+ unbindServiceOfUserLocked(prevUserId);
+
+ buildTvInputListLocked(mCurrentUserId, null);
+ buildTvContentRatingSystemListLocked(mCurrentUserId);
mMessageHandler
.obtainMessage(MessageHandler.MSG_SWITCH_CONTENT_RESOLVER,
- getContentResolverForUser(userId))
+ getContentResolverForUser(mCurrentUserId))
.sendToTarget();
}
}
@@ -590,6 +622,9 @@ public final class TvInputManagerService extends SystemService {
@GuardedBy("mLock")
private void unbindServiceOfUserLocked(int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "unbindServiceOfUserLocked: user " + userId);
+ }
UserState userState = getUserStateLocked(userId);
if (userState == null) {
return;
@@ -600,7 +635,12 @@ public final class TvInputManagerService extends SystemService {
ServiceState serviceState = userState.serviceStateMap.get(component);
if (serviceState != null && serviceState.sessionTokens.isEmpty()) {
unbindService(serviceState);
- it.remove();
+ if (!serviceState.isHardware) {
+ it.remove();
+ } else {
+ serviceState.hardwareInputMap.clear();
+ serviceState.needInit = true;
+ }
}
}
}
@@ -774,7 +814,7 @@ public final class TvInputManagerService extends SystemService {
boolean shouldBind;
if (userId == mCurrentUserId || mRunningProfiles.contains(userId)) {
shouldBind = !serviceState.sessionTokens.isEmpty()
- || (serviceState.isHardware && serviceState.neverConnected);
+ || (serviceState.isHardware && serviceState.needInit);
} else {
// For a non-current user,
// if sessionTokens is not empty, it contains recording sessions only
@@ -3404,13 +3444,13 @@ public final class TvInputManagerService extends SystemService {
private ServiceCallback callback;
private boolean bound;
private boolean reconnecting;
- private boolean neverConnected;
+ private boolean needInit;
private ServiceState(ComponentName component, int userId) {
this.component = component;
this.connection = new InputServiceConnection(component, userId);
this.isHardware = hasHardwarePermission(mContext.getPackageManager(), component);
- this.neverConnected = true;
+ this.needInit = true;
}
}
@@ -3618,11 +3658,9 @@ public final class TvInputManagerService extends SystemService {
}
ComponentName component = mTvInputHardwareManager.getInputMap().get(inputId).getComponent();
ServiceState serviceState = getServiceStateLocked(component, userId);
- boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
- if (removed) {
- buildTvInputListLocked(userId, null);
- mTvInputHardwareManager.removeHardwareInput(inputId);
- }
+ serviceState.hardwareInputMap.remove(inputId);
+ buildTvInputListLocked(userId, null);
+ mTvInputHardwareManager.removeHardwareInput(inputId);
}
private final class InputServiceConnection implements ServiceConnection {
@@ -3648,7 +3686,7 @@ public final class TvInputManagerService extends SystemService {
}
ServiceState serviceState = userState.serviceStateMap.get(mComponent);
serviceState.service = ITvInputService.Stub.asInterface(service);
- serviceState.neverConnected = false;
+ serviceState.needInit = false;
// Register a callback, if we need to.
if (serviceState.isHardware && serviceState.callback == null) {
@@ -3841,9 +3879,12 @@ public final class TvInputManagerService extends SystemService {
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- Slog.d(TAG, "ServiceCallback: removeHardwareInput, inputId: " + inputId +
- " by " + mComponent + ", userId: " + mUserId);
- removeHardwareInputLocked(inputId, mUserId);
+ if (mUserId == mCurrentUserId) {
+ Slog.d(TAG,
+ "ServiceCallback: removeHardwareInput, inputId: " + inputId + " by "
+ + mComponent + ", userId: " + mUserId);
+ removeHardwareInputLocked(inputId, mUserId);
+ }
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -4578,6 +4619,11 @@ public final class TvInputManagerService extends SystemService {
private final class HardwareListener implements TvInputHardwareManager.Listener {
@Override
public void onStateChanged(String inputId, int state) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "onStateChanged: inputId " + (inputId != null ? inputId : "null")
+ + ", state " + state);
+ }
synchronized (mLock) {
setStateLocked(inputId, state, mCurrentUserId);
}
@@ -4585,6 +4631,11 @@ public final class TvInputManagerService extends SystemService {
@Override
public void onHardwareDeviceAdded(TvInputHardwareInfo info) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "onHardwareDeviceAdded: TvInputHardwareInfo "
+ + (info != null ? info.toString() : "null"));
+ }
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
@@ -4607,6 +4658,11 @@ public final class TvInputManagerService extends SystemService {
@Override
public void onHardwareDeviceRemoved(TvInputHardwareInfo info) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "onHardwareDeviceRemoved: TvInputHardwareInfo "
+ + (info != null ? info.toString() : "null"));
+ }
synchronized (mLock) {
String relatedInputId =
mTvInputHardwareManager.getHardwareInputIdMap().get(info.getDeviceId());
@@ -4634,6 +4690,11 @@ public final class TvInputManagerService extends SystemService {
@Override
public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "onHdmiDeviceAdded: HdmiDeviceInfo "
+ + (deviceInfo != null ? deviceInfo.toString() : "null"));
+ }
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(mCurrentUserId);
// Broadcast the event to all hardware inputs.
@@ -4656,6 +4717,11 @@ public final class TvInputManagerService extends SystemService {
@Override
public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "onHdmiDeviceRemoved: HdmiDeviceInfo "
+ + (deviceInfo != null ? deviceInfo.toString() : "null"));
+ }
synchronized (mLock) {
String relatedInputId =
mTvInputHardwareManager.getHdmiInputIdMap().get(deviceInfo.getId());
@@ -4683,6 +4749,12 @@ public final class TvInputManagerService extends SystemService {
@Override
public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo deviceInfo) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "onHdmiDeviceUpdated: inputId " + (inputId != null ? inputId : "null")
+ + ", deviceInfo: "
+ + (deviceInfo != null ? deviceInfo.toString() : "null"));
+ }
synchronized (mLock) {
Integer state;
switch (deviceInfo.getDevicePowerStatus()) {
diff --git a/services/core/java/com/android/server/uri/NeededUriGrants.java b/services/core/java/com/android/server/uri/NeededUriGrants.java
index 8c8f55304fbb..2fe61e00c97e 100644
--- a/services/core/java/com/android/server/uri/NeededUriGrants.java
+++ b/services/core/java/com/android/server/uri/NeededUriGrants.java
@@ -17,10 +17,13 @@
package com.android.server.uri;
import android.util.ArraySet;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.server.am.NeededUriGrantsProto;
+import java.util.Objects;
+
/** List of {@link GrantUri} a process needs. */
public class NeededUriGrants {
final String targetPkg;
@@ -35,6 +38,20 @@ public class NeededUriGrants {
this.uris = new ArraySet<>();
}
+ public void merge(NeededUriGrants other) {
+ if (other == null) return;
+ if (!Objects.equals(this.targetPkg, other.targetPkg)
+ || this.targetUid != other.targetUid || this.flags != other.flags) {
+ Slog.wtf("NeededUriGrants",
+ "The other NeededUriGrants does not share the same targetUid, targetPkg or "
+ + "flags. It cannot be merged into this NeededUriGrants. This "
+ + "NeededUriGrants: " + this.toStringWithoutUri()
+ + ". Other NeededUriGrants: " + other.toStringWithoutUri());
+ } else {
+ this.uris.addAll(other.uris);
+ }
+ }
+
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
proto.write(NeededUriGrantsProto.TARGET_PACKAGE, targetPkg);
@@ -47,4 +64,12 @@ public class NeededUriGrants {
}
proto.end(token);
}
+
+ public String toStringWithoutUri() {
+ return "NeededUriGrants{" +
+ "targetPkg='" + targetPkg + '\'' +
+ ", targetUid=" + targetUid +
+ ", flags=" + flags +
+ '}';
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index fbcc856d0974..d192e64c897f 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -21,6 +21,8 @@ import android.annotation.Nullable;
import android.os.CombinedVibration;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
+import android.os.VibratorInfo;
+import android.os.vibrator.Flags;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.SparseArray;
@@ -145,19 +147,30 @@ final class HalVibration extends Vibration {
originalEffect, mScaleLevel, mAdaptiveScale);
}
- /**
- * Returns true if this vibration can pipeline with the specified one.
- *
- * <p>Note that currently, repeating vibrations can't pipeline with following vibrations,
- * because the cancel() call to stop the repetition will cancel a pending vibration too. This
- * can be changed if we have a use-case to reason around behavior for. It may also be nice to
- * pipeline very short vibrations together, regardless of the flag.
- */
- public boolean canPipelineWith(HalVibration vib) {
- return callerInfo.uid == vib.callerInfo.uid && callerInfo.attrs.isFlagSet(
- VibrationAttributes.FLAG_PIPELINED_EFFECT)
- && vib.callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT)
- && (mOriginalEffect.getDuration() != Long.MAX_VALUE);
+ /** Returns true if this vibration can pipeline with the specified one. */
+ public boolean canPipelineWith(HalVibration vib,
+ @Nullable SparseArray<VibratorInfo> vibratorInfos, int durationThresholdMs) {
+ long effectDuration = Flags.vibrationPipelineEnabled() && (vibratorInfos != null)
+ ? mEffectToPlay.getDuration(vibratorInfos)
+ : mEffectToPlay.getDuration();
+ if (effectDuration == Long.MAX_VALUE) {
+ // Repeating vibrations can't pipeline with following vibrations, because the cancel()
+ // call to stop the repetition will cancel a pending vibration too. This can be changed
+ // if we have a use-case, requiring changes to how pipelined vibrations are cancelled.
+ return false;
+ }
+ if (Flags.vibrationPipelineEnabled()
+ && (effectDuration > 0) && (effectDuration < durationThresholdMs)) {
+ // Duration is known and it's less than the pipeline threshold, so allow it.
+ // No need to check UID, as we want to avoid cancelling any short effect and let the
+ // vibrator hardware gracefully finish the vibration.
+ return true;
+ }
+ // Check the same app is requesting multiple vibrations with the pipeline flag,
+ // independently of the effect durations.
+ return callerInfo.uid == vib.callerInfo.uid
+ && callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT)
+ && vib.callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_PIPELINED_EFFECT);
}
private void fillFallbacksForEffect(CombinedVibration effect,
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 9b7bdece69f9..7d5d34dbf7ab 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -168,12 +168,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@VisibleForTesting
final VibrationSettings mVibrationSettings;
+ private final VibrationConfig mVibrationConfig;
private final VibrationScaler mVibrationScaler;
private final VibratorControlService mVibratorControlService;
private final InputDeviceDelegate mInputDeviceDelegate;
private final DeviceAdapter mDeviceAdapter;
@GuardedBy("mLock")
+ @Nullable private SparseArray<VibratorInfo> mVibratorInfos;
+ @GuardedBy("mLock")
@Nullable private VibratorInfo mCombinedVibratorInfo;
@GuardedBy("mLock")
@Nullable private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider;
@@ -247,9 +250,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mHandler = injector.createHandler(Looper.myLooper());
mFrameworkStatsLogger = injector.getFrameworkStatsLogger(mHandler);
- VibrationConfig vibrationConfig = new VibrationConfig(context.getResources());
- mVibrationSettings = new VibrationSettings(mContext, mHandler, vibrationConfig);
- mVibrationScaler = new VibrationScaler(vibrationConfig, mVibrationSettings);
+ mVibrationConfig = new VibrationConfig(context.getResources());
+ mVibrationSettings = new VibrationSettings(mContext, mHandler, mVibrationConfig);
+ mVibrationScaler = new VibrationScaler(mVibrationConfig, mVibrationSettings);
mVibratorControlService = new VibratorControlService(mContext,
injector.createVibratorControllerHolder(), mVibrationScaler, mVibrationSettings,
mFrameworkStatsLogger, mLock);
@@ -295,7 +298,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mVibratorIds = vibratorIds;
mVibrators = new SparseArray<>(mVibratorIds.length);
for (int vibratorId : vibratorIds) {
- mVibrators.put(vibratorId, injector.createVibratorController(vibratorId, listener));
+ VibratorController vibratorController =
+ injector.createVibratorController(vibratorId, listener);
+ mVibrators.put(vibratorId, vibratorController);
}
}
@@ -334,6 +339,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
mVibrators.valueAt(i).reloadVibratorInfoIfNeeded();
}
+ synchronized (mLock) {
+ mVibratorInfos = transformAllVibratorsLocked(VibratorController::getVibratorInfo);
+ VibratorInfo[] infos = new VibratorInfo[mVibratorInfos.size()];
+ for (int i = 0; i < mVibratorInfos.size(); i++) {
+ infos[i] = mVibratorInfos.valueAt(i);
+ }
+ mCombinedVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, infos);
+ }
+
mVibrationSettings.onSystemReady();
mInputDeviceDelegate.onSystemReady();
@@ -633,7 +647,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
endExternalVibrateLocked(Status.CANCELLED_SUPERSEDED, callerInfo,
/* continueExternalControl= */ false);
} else if (mCurrentVibration != null) {
- if (mCurrentVibration.getVibration().canPipelineWith(vib)) {
+ if (mCurrentVibration.getVibration().canPipelineWith(vib, mVibratorInfos,
+ mVibrationConfig.getVibrationPipelineMaxDurationMs())) {
// Don't cancel the current vibration if it's pipeline-able.
// Note that if there is a pending next vibration that can't be
// pipelined, it will have already cancelled the current one, so we
@@ -1871,33 +1886,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
+ @Nullable
private VibratorInfo getCombinedVibratorInfo() {
synchronized (mLock) {
- // Used a cached resolving vibrator if one exists.
- if (mCombinedVibratorInfo != null) {
- return mCombinedVibratorInfo;
- }
-
- // Return an empty resolving vibrator if the service has no vibrator.
- if (mVibratorIds.length == 0) {
- return mCombinedVibratorInfo = VibratorInfo.EMPTY_VIBRATOR_INFO;
- }
-
- // Combine the vibrator infos of all the service's vibrator to create a single resolving
- // vibrator that is based on the combined info.
- VibratorInfo[] infos = new VibratorInfo[mVibratorIds.length];
- for (int i = 0; i < mVibratorIds.length; i++) {
- VibratorInfo info = getVibratorInfo(mVibratorIds[i]);
- // If any one of the service's vibrator does not have a valid vibrator info, stop
- // trying to create and cache a combined resolving vibrator. Combine the infos only
- // when infos for all vibrators are available.
- if (info == null) {
- return null;
- }
- infos[i] = info;
- }
-
- return mCombinedVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, infos);
+ // This is only initialized at system ready, when all vibrator infos are fully loaded.
+ return mCombinedVibratorInfo;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index ae30fcde39e1..d119a08b0c85 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -442,8 +442,6 @@ class ActivityClientController extends IActivityClientController.Stub {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
- mService.mAmInternal.addCreatorToken(resultData);
-
final ActivityRecord r;
synchronized (mGlobalLock) {
r = ActivityRecord.isInRootTaskLocked(token);
@@ -502,6 +500,8 @@ class ActivityClientController extends IActivityClientController.Stub {
r.app.setLastActivityFinishTimeIfNeeded(SystemClock.uptimeMillis());
}
+ mService.mAmInternal.addCreatorToken(resultData, r.packageName);
+
final long origId = Binder.clearCallingIdentity();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity");
try {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a44eb48d7836..460de01a7d1d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -285,9 +285,6 @@ import android.app.servertransaction.StopActivityItem;
import android.app.servertransaction.TopResumedActivityChangeItem;
import android.app.servertransaction.TransferSplashScreenViewStateItem;
import android.app.usage.UsageEvents.Event;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
-import android.compat.annotation.Overridable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -467,11 +464,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// finished destroying itself.
private static final int DESTROY_TIMEOUT = 10 * 1000;
- @ChangeId
- @Overridable
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
- static final long UNIVERSAL_RESIZABLE_BY_DEFAULT = 357141415;
-
final ActivityTaskManagerService mAtmService;
final ActivityCallerState mCallerState;
@NonNull
@@ -3192,7 +3184,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
&& mDisplayContent != null && mDisplayContent.getConfiguration()
.smallestScreenWidthDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
&& mDisplayContent.getIgnoreOrientationRequest()
- && info.isChangeEnabled(UNIVERSAL_RESIZABLE_BY_DEFAULT);
+ && info.isChangeEnabled(ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT);
if (!compatEnabled && !mWmService.mConstants.mIgnoreActivityOrientationRequest) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 0580d4a5a4a3..c1f5a27b81e7 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
+import static com.android.server.wm.ActivityStarter.Request.DEFAULT_INTENT_CREATOR_UID;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
@@ -441,6 +442,17 @@ public class ActivityStartController {
0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid,
callingPid);
aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
+ int creatorUid = DEFAULT_INTENT_CREATOR_UID;
+ String creatorPackage = null;
+ if (ActivityManagerService.IntentCreatorToken.isValid(intent)) {
+ ActivityManagerService.IntentCreatorToken creatorToken =
+ (ActivityManagerService.IntentCreatorToken) intent.getCreatorToken();
+ if (creatorToken.getCreatorUid() != filterCallingUid) {
+ creatorUid = creatorToken.getCreatorUid();
+ creatorPackage = creatorToken.getCreatorPackage();
+ }
+ // leave creatorUid as -1 if the intent creator is the same as the launcher
+ }
if (aInfo != null) {
try {
@@ -454,6 +466,24 @@ public class ActivityStartController {
return START_CANCELED;
}
+ if (creatorUid != DEFAULT_INTENT_CREATOR_UID) {
+ try {
+ NeededUriGrants creatorIntentGrants = mSupervisor.mService.mUgmInternal
+ .checkGrantUriPermissionFromIntent(intent, creatorUid,
+ aInfo.applicationInfo.packageName,
+ UserHandle.getUserId(aInfo.applicationInfo.uid));
+ if (intentGrants == null) {
+ intentGrants = creatorIntentGrants;
+ } else {
+ intentGrants.merge(creatorIntentGrants);
+ }
+ } catch (SecurityException securityException) {
+ ActivityStarter.logForIntentRedirect(
+ "Creator URI Grant Caused Exception.", intent, creatorUid,
+ creatorPackage, filterCallingUid, callingPackage);
+ // TODO b/368559093 - rethrow the securityException.
+ }
+ }
if ((aInfo.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
throw new IllegalArgumentException(
@@ -477,6 +507,8 @@ public class ActivityStartController {
.setCallingUid(callingUid)
.setCallingPackage(callingPackage)
.setCallingFeatureId(callingFeatureId)
+ .setIntentCreatorUid(creatorUid)
+ .setIntentCreatorPackage(creatorPackage)
.setRealCallingPid(realCallingPid)
.setRealCallingUid(realCallingUid)
.setActivityOptions(checkedOptions)
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 87fa62ac0e3b..5d3ae54f0934 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -86,6 +86,7 @@ import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENS
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.window.flags.Flags.balDontBringExistingBackgroundTaskStackToFg;
import android.annotation.IntDef;
@@ -131,6 +132,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.protolog.ProtoLog;
+import com.android.server.am.ActivityManagerService.IntentCreatorToken;
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.InstantAppResolver;
import com.android.server.pm.PackageArchiver;
@@ -383,6 +385,7 @@ class ActivityStarter {
private static final int DEFAULT_CALLING_PID = 0;
static final int DEFAULT_REAL_CALLING_UID = -1;
static final int DEFAULT_REAL_CALLING_PID = 0;
+ static final int DEFAULT_INTENT_CREATOR_UID = -1;
IApplicationThread caller;
Intent intent;
@@ -403,6 +406,8 @@ class ActivityStarter {
@Nullable String callingFeatureId;
int realCallingPid = DEFAULT_REAL_CALLING_PID;
int realCallingUid = DEFAULT_REAL_CALLING_UID;
+ int intentCreatorUid = DEFAULT_INTENT_CREATOR_UID;
+ String intentCreatorPackage;
int startFlags;
SafeActivityOptions activityOptions;
boolean ignoreTargetSecurity;
@@ -463,6 +468,8 @@ class ActivityStarter {
callingPid = DEFAULT_CALLING_PID;
callingUid = DEFAULT_CALLING_UID;
callingPackage = null;
+ intentCreatorUid = DEFAULT_INTENT_CREATOR_UID;
+ intentCreatorPackage = null;
callingFeatureId = null;
realCallingPid = DEFAULT_REAL_CALLING_PID;
realCallingUid = DEFAULT_REAL_CALLING_UID;
@@ -555,12 +562,14 @@ class ActivityStarter {
// "resolved" calling UID, where we try our best to identify the
// actual caller that is starting this activity
int resolvedCallingUid = callingUid;
+ String resolvedCallingPackage = callingPackage;
if (caller != null) {
synchronized (supervisor.mService.mGlobalLock) {
final WindowProcessController callerApp = supervisor.mService
.getProcessController(caller);
if (callerApp != null) {
resolvedCallingUid = callerApp.mInfo.uid;
+ resolvedCallingPackage = callerApp.mInfo.packageName;
}
}
}
@@ -596,7 +605,23 @@ class ActivityStarter {
// Collect information about the target of the Intent.
activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags,
profilerInfo);
-
+ // Check if the Intent was redirected
+ if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN)
+ != 0) {
+ ActivityStarter.logForIntentRedirect(
+ "Unparceled intent does not have a creator token set.", intent,
+ intentCreatorUid,
+ intentCreatorPackage, resolvedCallingUid, resolvedCallingPackage);
+ // TODO b/368559093 - eventually ramp up to throw SecurityException
+ }
+ if (IntentCreatorToken.isValid(intent)) {
+ IntentCreatorToken creatorToken = (IntentCreatorToken) intent.getCreatorToken();
+ if (creatorToken.getCreatorUid() != resolvedCallingUid) {
+ intentCreatorUid = creatorToken.getCreatorUid();
+ intentCreatorPackage = creatorToken.getCreatorPackage();
+ }
+ // leave intentCreatorUid as -1 if the intent creator is the same as the launcher
+ }
// Carefully collect grants without holding lock
if (activityInfo != null) {
if (android.security.Flags.contentUriPermissionApis()) {
@@ -606,11 +631,52 @@ class ActivityStarter {
UserHandle.getUserId(activityInfo.applicationInfo.uid),
activityInfo.requireContentUriPermissionFromCaller,
/* requestHashCode */ this.hashCode());
+ if (intentCreatorUid != DEFAULT_INTENT_CREATOR_UID) {
+ try {
+ NeededUriGrants creatorIntentGrants = supervisor.mService.mUgmInternal
+ .checkGrantUriPermissionFromIntent(intent, intentCreatorUid,
+ activityInfo.applicationInfo.packageName,
+ UserHandle.getUserId(activityInfo.applicationInfo.uid),
+ activityInfo.requireContentUriPermissionFromCaller,
+ /* requestHashCode */ this.hashCode());
+ if (intentGrants == null) {
+ intentGrants = creatorIntentGrants;
+ } else {
+ intentGrants.merge(creatorIntentGrants);
+ }
+ } catch (SecurityException securityException) {
+ ActivityStarter.logForIntentRedirect(
+ "Creator URI Grant Caused Exception.", intent, intentCreatorUid,
+ intentCreatorPackage, resolvedCallingUid,
+ resolvedCallingPackage);
+ // TODO b/368559093 - rethrow the securityException.
+ }
+ }
} else {
intentGrants = supervisor.mService.mUgmInternal
.checkGrantUriPermissionFromIntent(intent, resolvedCallingUid,
activityInfo.applicationInfo.packageName,
UserHandle.getUserId(activityInfo.applicationInfo.uid));
+ if (intentCreatorUid != DEFAULT_INTENT_CREATOR_UID && intentGrants != null) {
+ try {
+ NeededUriGrants creatorIntentGrants = supervisor.mService.mUgmInternal
+ .checkGrantUriPermissionFromIntent(intent, intentCreatorUid,
+ activityInfo.applicationInfo.packageName,
+ UserHandle.getUserId(
+ activityInfo.applicationInfo.uid));
+ if (intentGrants == null) {
+ intentGrants = creatorIntentGrants;
+ } else {
+ intentGrants.merge(creatorIntentGrants);
+ }
+ } catch (SecurityException securityException) {
+ ActivityStarter.logForIntentRedirect(
+ "Creator URI Grant Caused Exception.", intent, intentCreatorUid,
+ intentCreatorPackage, resolvedCallingUid,
+ resolvedCallingPackage);
+ // TODO b/368559093 - rethrow the securityException.
+ }
+ }
}
}
}
@@ -977,7 +1043,9 @@ class ActivityStarter {
int requestCode = request.requestCode;
int callingPid = request.callingPid;
int callingUid = request.callingUid;
- String callingPackage = request.callingPackage;
+ int intentCreatorUid = request.intentCreatorUid;
+ String intentCreatorPackage = request.intentCreatorPackage;
+ String intentCallingPackage = request.callingPackage;
String callingFeatureId = request.callingFeatureId;
final int realCallingPid = request.realCallingPid;
final int realCallingUid = request.realCallingUid;
@@ -1062,7 +1130,7 @@ class ActivityStarter {
// launched in the app flow to redirect to an activity picked by the user, where
// we want the final activity to consider it to have been launched by the
// previous app activity.
- callingPackage = sourceRecord.launchedFromPackage;
+ intentCallingPackage = sourceRecord.launchedFromPackage;
callingFeatureId = sourceRecord.launchedFromFeatureId;
}
}
@@ -1084,7 +1152,7 @@ class ActivityStarter {
if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) {
err = packageArchiver
.requestUnarchiveOnActivityStart(
- intent, callingPackage, mRequest.userId, realCallingUid);
+ intent, intentCallingPackage, mRequest.userId, realCallingUid);
}
}
}
@@ -1143,7 +1211,7 @@ class ActivityStarter {
boolean abort;
try {
abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
- requestCode, callingPid, callingUid, callingPackage, callingFeatureId,
+ requestCode, callingPid, callingUid, intentCallingPackage, callingFeatureId,
request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord,
resultRootTask);
} catch (SecurityException e) {
@@ -1171,7 +1239,47 @@ class ActivityStarter {
abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,
- callingPackage);
+ intentCallingPackage);
+
+ if (intentCreatorUid != Request.DEFAULT_INTENT_CREATOR_UID) {
+ try {
+ if (!mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
+ requestCode, 0, intentCreatorUid, intentCreatorPackage, "",
+ request.ignoreTargetSecurity, inTask != null, null, resultRecord,
+ resultRootTask)) {
+ logForIntentRedirect("Creator checkStartAnyActivityPermission Caused abortion.",
+ intent, intentCreatorUid, intentCreatorPackage, callingUid,
+ intentCallingPackage);
+ // TODO b/368559093 - set abort to true.
+ // abort = true;
+ }
+ } catch (SecurityException e) {
+ logForIntentRedirect("Creator checkStartAnyActivityPermission Caused Exception.",
+ intent, intentCreatorUid, intentCreatorPackage, callingUid,
+ intentCallingPackage);
+ // TODO b/368559093 - rethrow the exception.
+ //throw e;
+ }
+ if (!mService.mIntentFirewall.checkStartActivity(intent, intentCreatorUid, 0,
+ resolvedType, aInfo.applicationInfo)) {
+ logForIntentRedirect("Creator IntentFirewall.checkStartActivity Caused abortion.",
+ intent, intentCreatorUid, intentCreatorPackage, callingUid,
+ intentCallingPackage);
+ // TODO b/368559093 - set abort to true.
+ // abort = true;
+ }
+
+ if (!mService.getPermissionPolicyInternal().checkStartActivity(intent, intentCreatorUid,
+ intentCreatorPackage)) {
+ logForIntentRedirect(
+ "Creator PermissionPolicyService.checkStartActivity Caused abortion.",
+ intent, intentCreatorUid, intentCreatorPackage, callingUid,
+ intentCallingPackage);
+ // TODO b/368559093 - set abort to true.
+ // abort = true;
+ }
+ intent.removeCreatorTokenInfo();
+ }
// Merge the two options bundles, while realCallerOptions takes precedence.
ActivityOptions checkedOptions = options != null
@@ -1188,7 +1296,7 @@ class ActivityStarter {
balController.checkBackgroundActivityStart(
callingUid,
callingPid,
- callingPackage,
+ intentCallingPackage,
realCallingUid,
realCallingPid,
callerApp,
@@ -1209,7 +1317,7 @@ class ActivityStarter {
if (request.allowPendingRemoteAnimationRegistryLookup) {
checkedOptions = mService.getActivityStartController()
.getPendingRemoteAnimationRegistry()
- .overrideOptionsIfNeeded(callingPackage, checkedOptions);
+ .overrideOptionsIfNeeded(intentCallingPackage, checkedOptions);
}
if (mService.mController != null) {
try {
@@ -1225,7 +1333,8 @@ class ActivityStarter {
final TaskDisplayArea suggestedLaunchDisplayArea =
computeSuggestedLaunchDisplayArea(inTask, sourceRecord, checkedOptions);
- mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage,
+ mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags,
+ intentCallingPackage,
callingFeatureId);
if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment,
callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea)) {
@@ -1263,7 +1372,8 @@ class ActivityStarter {
if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
aInfo.packageName, userId)) {
final IIntentSender target = mService.getIntentSenderLocked(
- ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, callingFeatureId,
+ ActivityManager.INTENT_SENDER_ACTIVITY, intentCallingPackage,
+ callingFeatureId,
callingUid, userId, null, null, 0, new Intent[]{intent},
new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
| PendingIntent.FLAG_ONE_SHOT, null);
@@ -1326,7 +1436,8 @@ class ActivityStarter {
// app [on install success].
if (rInfo != null && rInfo.auxiliaryInfo != null) {
intent = createLaunchIntent(rInfo.auxiliaryInfo, request.ephemeralIntent,
- callingPackage, callingFeatureId, verificationBundle, resolvedType, userId);
+ intentCallingPackage, callingFeatureId, verificationBundle, resolvedType,
+ userId);
resolvedType = null;
callingUid = realCallingUid;
callingPid = realCallingPid;
@@ -1349,7 +1460,7 @@ class ActivityStarter {
.setCaller(callerApp)
.setLaunchedFromPid(callingPid)
.setLaunchedFromUid(callingUid)
- .setLaunchedFromPackage(callingPackage)
+ .setLaunchedFromPackage(intentCallingPackage)
.setLaunchedFromFeature(callingFeatureId)
.setIntent(intent)
.setResolvedType(resolvedType)
@@ -2786,10 +2897,22 @@ class ActivityStarter {
}
}
- if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
- && ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 || mSourceRecord == null)) {
- // ignore the flag if there is no the sourceRecord or without new_task flag
- mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT;
+ if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
+ final boolean hasNewTaskFlag = (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0;
+ if (!hasNewTaskFlag || mSourceRecord == null) {
+ // ignore the flag if there is no the sourceRecord or without new_task flag
+ Slog.w(TAG_WM, !hasNewTaskFlag
+ ? "Launch adjacent ignored due to missing NEW_TASK"
+ : "Launch adjacent ignored due to missing source activity");
+ mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT;
+ }
+ // Ensure that the source task or its parents has not disabled launch-adjacent
+ if (mSourceRecord != null && mSourceRecord.getTask() != null &&
+ mSourceRecord.getTask().isLaunchAdjacentDisabled()) {
+ Slog.w(TAG_WM, "Launch adjacent blocked by source task or ancestor");
+ mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT;
+ }
+
}
}
@@ -3295,6 +3418,16 @@ class ActivityStarter {
return this;
}
+ ActivityStarter setIntentCreatorUid(int uid) {
+ mRequest.intentCreatorUid = uid;
+ return this;
+ }
+
+ ActivityStarter setIntentCreatorPackage(String intentCreatorPackage) {
+ mRequest.intentCreatorPackage = intentCreatorPackage;
+ return this;
+ }
+
/**
* Sets the pid of the caller who requested to launch the activity.
*
@@ -3454,4 +3587,19 @@ class ActivityStarter {
pw.print(" mInTaskFragment=");
pw.println(mInTaskFragment);
}
+
+ static void logForIntentRedirect(String message, Intent intent, int intentCreatorUid,
+ String intentCreatorPackage, int callingUid, String callingPackage) {
+ String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid,
+ intentCreatorPackage, callingUid, callingPackage);
+ Slog.wtf(TAG, msg);
+ }
+
+ private static String getIntentRedirectPreventedLogMessage(String message, Intent intent,
+ int intentCreatorUid, String intentCreatorPackage, int callingUid,
+ String callingPackage) {
+ return "[IntentRedirect]" + message + " intentCreatorUid: " + intentCreatorUid
+ + "; intentCreatorPackage: " + intentCreatorPackage + "; callingUid: " + callingUid
+ + "; callingPackage: " + callingPackage + "; intent: " + intent;
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 3d6b64b2e536..3560565ce9cd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -589,7 +589,7 @@ public abstract class ActivityTaskManagerInternal {
* sensitive environment.
*/
public abstract TaskSnapshot getTaskSnapshotBlocking(int taskId,
- boolean isLowResolution);
+ boolean isLowResolution, @TaskSnapshot.ReferenceFlags int usage);
/** Returns true if uid is considered foreground for activity start purposes. */
public abstract boolean isUidForeground(int uid);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 3dfc8f4e5bf9..5339753624d8 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1228,7 +1228,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions) {
- mAmInternal.addCreatorToken(intent);
+ mAmInternal.addCreatorToken(intent, callingPackage);
return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
@@ -1243,7 +1243,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
enforceNotIsolatedCaller(reason);
if (intents != null) {
for (Intent intent : intents) {
- mAmInternal.addCreatorToken(intent);
+ mAmInternal.addCreatorToken(intent, callingPackage);
}
}
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
@@ -1275,7 +1275,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Nullable String callingFeatureId, Intent intent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
- mAmInternal.addCreatorToken(intent);
+ mAmInternal.addCreatorToken(intent, callingPackage);
final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions);
assertPackageMatchesCallingUid(callingPackage);
@@ -1330,7 +1330,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
// Remove existing mismatch flag so it can be properly updated later
fillInIntent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
- mAmInternal.addCreatorToken(fillInIntent);
}
if (!(target instanceof PendingIntentRecord)) {
@@ -1339,6 +1338,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
PendingIntentRecord pir = (PendingIntentRecord) target;
+ if (fillInIntent != null) {
+ mAmInternal.addCreatorToken(fillInIntent, pir.getPackageName());
+ }
+
synchronized (mGlobalLock) {
// If this is coming from the currently resumed activity, it is
// effectively saying that app switches are allowed at this point.
@@ -1349,6 +1352,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mAppSwitchesState = APP_SWITCH_ALLOW;
}
}
+
return pir.sendInner(caller, 0, fillInIntent, resolvedType, allowlistToken, null, null,
resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions);
}
@@ -1361,8 +1365,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
- mAmInternal.addCreatorToken(intent);
-
SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
synchronized (mGlobalLock) {
@@ -1376,6 +1378,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
SafeActivityOptions.abort(options);
return false;
}
+
+ mAmInternal.addCreatorToken(intent, r.packageName);
+
intent = new Intent(intent);
// Remove existing mismatch flag so it can be properly updated later
intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
@@ -3946,6 +3951,28 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
+ private TaskSnapshot getTaskSnapshotInner(int taskId, boolean isLowResolution,
+ @TaskSnapshot.ReferenceFlags int usage) {
+ final Task task;
+ synchronized (mGlobalLock) {
+ task = mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
+ if (task == null) {
+ Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
+ return null;
+ }
+ // Try to load snapshot from cache first, and add reference if the snapshot is in cache.
+ final TaskSnapshot snapshot = mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
+ task.mUserId, false /* restoreFromDisk */, isLowResolution);
+ if (snapshot != null) {
+ snapshot.addReference(usage);
+ return snapshot;
+ }
+ }
+ // Don't call this while holding the lock as this operation might hit the disk.
+ return mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
+ task.mUserId, true /* restoreFromDisk */, isLowResolution);
+ }
+
@Override
public TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution) {
mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
@@ -7274,8 +7301,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public TaskSnapshot getTaskSnapshotBlocking(
- int taskId, boolean isLowResolution) {
- return ActivityTaskManagerService.this.getTaskSnapshot(taskId, isLowResolution);
+ int taskId, boolean isLowResolution, @TaskSnapshot.ReferenceFlags int usage) {
+ return ActivityTaskManagerService.this.getTaskSnapshotInner(
+ taskId, isLowResolution, usage);
}
@Override
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 4d17ed24e734..eee4c86bc483 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -160,7 +160,7 @@ class AppTaskImpl extends IAppTask.Stub {
Intent intent, String resolvedType, Bundle bOptions) {
checkCallerOrSystemOrRoot();
mService.assertPackageMatchesCallingUid(callingPackage);
- mService.mAmInternal.addCreatorToken(intent);
+ mService.mAmInternal.addCreatorToken(intent, callingPackage);
int callingUser = UserHandle.getCallingUserId();
Task task;
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 94cd2e64b057..70f9ebb0e61e 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -34,6 +34,7 @@ import static com.android.server.wm.BackNavigationProto.LAST_BACK_TYPE;
import static com.android.server.wm.BackNavigationProto.MAIN_OPEN_ACTIVITY;
import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
+import static com.android.server.wm.WindowContainer.SYNC_STATE_NONE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1237,8 +1238,9 @@ class BackNavigationController {
}
allWindowDrawn &= next.mAppWindowDrawn;
}
- // Do not remove until transition ready.
- if (!activity.isVisible()) {
+ // Do not remove windowless surfaces if the transaction has not been applied.
+ if (activity.getSyncTransactionCommitCallbackDepth() > 0
+ || activity.mSyncState != SYNC_STATE_NONE) {
return;
}
if (allWindowDrawn) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index a380ba1a6f11..f9902cf0db9c 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -66,6 +66,7 @@ import android.app.BackgroundStartPrivileges;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.Overridable;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -129,6 +130,7 @@ public class BackgroundActivityStartController {
/** If enabled the creator will not allow BAL on its behalf by default. */
@ChangeId
@EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE)
+ @Overridable
private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR =
296478951;
public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED =
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 6a23aaa663ef..e9c6e93891df 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -274,34 +274,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
if (caller != controlTarget) {
if (Flags.refactorInsetsController()) {
if (isImeInputTarget(caller)) {
- // In case of the multi window mode, update the requestedVisibleTypes from
- // the controlTarget (=RemoteInsetsControlTarget) via DisplayImeController.
- // Then, trigger onRequestedVisibleTypesChanged for the controlTarget with
- // its new requested visibility for the IME
- boolean imeVisible = caller.isRequestedVisible(WindowInsets.Type.ime());
- if (controlTarget != null) {
- ImeTracker.forLogging().onProgress(statsToken,
- ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
- controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
- } else if (caller instanceof InsetsControlTarget) {
- // In case of a virtual display that cannot show the IME, the
- // controlTarget will be null here, as no controlTarget was set yet. In
- // that case, proceed similar to the multi window mode (fallback =
- // RemoteInsetsControlTarget of the default display)
- controlTarget = mDisplayContent.getImeHostOrFallback(
- ((InsetsControlTarget) caller).getWindow());
-
- if (controlTarget != caller) {
- ImeTracker.forLogging().onProgress(statsToken,
- ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
- controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
- } else {
- ImeTracker.forLogging().onFailed(statsToken,
- ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
- }
- }
-
- invokeOnImeRequestedChangedListener(caller, statsToken);
+ reportImeInputTargetStateToControlTarget(caller, controlTarget, statsToken);
} else {
// TODO(b/353463205) add ImeTracker?
}
@@ -332,14 +305,42 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
if (Flags.refactorInsetsController() && target != null) {
InsetsControlTarget imeControlTarget = getControlTarget();
if (target != imeControlTarget) {
- // If the targetWin is not the imeControlTarget (=RemoteInsetsControlTarget) let it
- // know about the new requestedVisibleTypes for the IME.
- if (imeControlTarget != null) {
- imeControlTarget.setImeInputTargetRequestedVisibility(
- (target.getRequestedVisibleTypes() & WindowInsets.Type.ime()) != 0);
- }
+ // TODO(b/353463205): start new request here?
+ reportImeInputTargetStateToControlTarget(target, imeControlTarget,
+ null /* statsToken */);
+ }
+ }
+ }
+
+ private void reportImeInputTargetStateToControlTarget(@NonNull InsetsTarget imeInsetsTarget,
+ InsetsControlTarget controlTarget, @Nullable ImeTracker.Token statsToken) {
+ // In case of the multi window mode, update the requestedVisibleTypes from
+ // the controlTarget (=RemoteInsetsControlTarget) via DisplayImeController.
+ // Then, trigger onRequestedVisibleTypesChanged for the controlTarget with
+ // its new requested visibility for the IME
+ boolean imeVisible = imeInsetsTarget.isRequestedVisible(WindowInsets.Type.ime());
+ if (controlTarget != null) {
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
+ controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
+ } else if (imeInsetsTarget instanceof InsetsControlTarget) {
+ // In case of a virtual display that cannot show the IME, the
+ // controlTarget will be null here, as no controlTarget was set yet. In
+ // that case, proceed similar to the multi window mode (fallback =
+ // RemoteInsetsControlTarget of the default display)
+ controlTarget = mDisplayContent.getImeHostOrFallback(
+ ((InsetsControlTarget) imeInsetsTarget).getWindow());
+
+ if (controlTarget != imeInsetsTarget) {
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
+ controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
+ } else {
+ ImeTracker.forLogging().onFailed(statsToken,
+ ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
}
}
+ invokeOnImeRequestedChangedListener(imeInsetsTarget, statsToken);
}
// TODO(b/353463205) check callers to see if we can make statsToken @NonNull
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index a4fe0647ea79..9d21183c6c03 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -131,7 +131,9 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
final boolean changed = !com.android.window.flags.Flags.filterIrrelevantInputDeviceChange()
|| updateLastInputConfigurationSources();
- if (changed) {
+ // Even if the input devices are not changed, there could be other pending changes
+ // during booting. It's fine to apply earlier.
+ if (changed || !mService.mDisplayEnabled) {
synchronized (mService.mGlobalLock) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "inputDeviceConfigChanged");
mService.mRoot.forAllDisplays(DisplayContent::sendNewConfiguration);
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 6067a9972bbe..1d4d6eb82c44 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -409,7 +409,7 @@ class InsetsSourceProvider {
}
final Point position = getWindowFrameSurfacePosition();
if (!mPosition.equals(position)) {
- mPosition.set(position.x, position.y);
+ mPosition.set(position);
if (windowState != null && windowState.getWindowFrames().didFrameSizeChange()
&& windowState.mWinAnimator.getShown() && mWindowContainer.okToDisplay()) {
mHasPendingPosition = true;
@@ -553,6 +553,7 @@ class InsetsSourceProvider {
}
boolean initiallyVisible = mClientVisible;
final Point surfacePosition = getWindowFrameSurfacePosition();
+ mPosition.set(surfacePosition);
mAdapter = new ControlAdapter(surfacePosition);
if (mSource.getType() == WindowInsets.Type.ime()) {
if (android.view.inputmethod.Flags.refactorInsetsController()) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 00704b3525c5..4861341f830a 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -503,6 +503,11 @@ class Task extends TaskFragment {
boolean mIsTrimmableFromRecents;
/**
+ * Sets whether the launch-adjacent flag is respected or not for this task or its child tasks.
+ */
+ private boolean mLaunchAdjacentDisabled;
+
+ /**
* Bounds offset should be applied when calculating compatible configuration for apps targeting
* SDK level 34 or before.
*/
@@ -3802,6 +3807,9 @@ class Task extends TaskFragment {
pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
pw.print(prefix); pw.println(" isTrimmable=" + mIsTrimmableFromRecents);
+ if (mLaunchAdjacentDisabled) {
+ pw.println(prefix + "mLaunchAdjacentDisabled=true");
+ }
}
@Override
@@ -5734,9 +5742,9 @@ class Task extends TaskFragment {
}
private boolean canMoveTaskToBack(Task task) {
- // Checks whether a task is a child of this task because it can be reparetned when
+ // Checks whether a task is a child of this task because it can be reparented when
// transition is deferred.
- if (task != this && task.getParent() != this) {
+ if (task != this && !task.isDescendantOf(this)) {
return false;
}
@@ -6274,6 +6282,28 @@ class Task extends TaskFragment {
}
/**
+ * Sets this task and its children to disable respecting launch-adjacent.
+ */
+ void setLaunchAdjacentDisabled(boolean disabled) {
+ mLaunchAdjacentDisabled = disabled;
+ }
+
+ /**
+ * Returns whether this task or any of its ancestors have disabled respecting the
+ * launch-adjacent flag.
+ */
+ boolean isLaunchAdjacentDisabled() {
+ Task t = this;
+ while (t != null) {
+ if (t.mLaunchAdjacentDisabled) {
+ return true;
+ }
+ t = t.getParent().asTask();
+ }
+ return false;
+ }
+
+ /**
* Return true if the activityInfo has the same requiredDisplayCategory as this task.
*/
boolean isSameRequiredDisplayCategory(@NonNull ActivityInfo info) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 5dd3bbce4e96..2c71c1a1f4f3 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1074,6 +1074,8 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
final Task launchRootTask = Task.fromWindowContainerToken(options.getLaunchRootTask());
// We only allow this for created by organizer tasks.
if (launchRootTask != null && launchRootTask.mCreatedByOrganizer) {
+ Slog.i(TAG_WM, "Using launch root task from activity options: taskId="
+ + launchRootTask.mTaskId);
return launchRootTask;
}
}
@@ -1081,19 +1083,25 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
// Use launch-adjacent-flag-root if launching with launch-adjacent flag.
if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0
&& mLaunchAdjacentFlagRootTask != null) {
+ final Task launchAdjacentRootAdjacentTask =
+ mLaunchAdjacentFlagRootTask.getAdjacentTask();
if (sourceTask != null && (sourceTask == candidateTask
|| sourceTask.topRunningActivity() == null)) {
// Do nothing when task that is getting opened is same as the source or when
// the source is no-longer valid.
Slog.w(TAG_WM, "Ignoring LAUNCH_ADJACENT because adjacent source is gone.");
} else if (sourceTask != null
- && mLaunchAdjacentFlagRootTask.getAdjacentTask() != null
+ && launchAdjacentRootAdjacentTask != null
&& (sourceTask == mLaunchAdjacentFlagRootTask
|| sourceTask.isDescendantOf(mLaunchAdjacentFlagRootTask))) {
- // If the adjacent launch is coming from the same root, launch to
- // adjacent root instead.
- return mLaunchAdjacentFlagRootTask.getAdjacentTask();
+ // If the adjacent launch is coming from the same root that was specified as the
+ // launch-adjacent task, so instead we launch to its adjacent root instead.
+ Slog.i(TAG_WM, "Using adjacent-to specified launch-adjacent task: taskId="
+ + launchAdjacentRootAdjacentTask.mTaskId);
+ return launchAdjacentRootAdjacentTask;
} else {
+ Slog.i(TAG_WM, "Using specified launch-adjacent task: taskId="
+ + mLaunchAdjacentFlagRootTask.mTaskId);
return mLaunchAdjacentFlagRootTask;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 82c7a9350eca..166d74b132bd 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -69,6 +69,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
@@ -1450,6 +1451,17 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
task.setTrimmableFromRecents(hop.isTrimmableFromRecents());
break;
}
+ case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT: {
+ final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
+ final Task task = container != null ? container.asTask() : null;
+ if (task == null || !task.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on unknown or detached container: "
+ + container);
+ break;
+ }
+ task.setLaunchAdjacentDisabled(hop.isLaunchAdjacentDisabled());
+ break;
+ }
case HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION: {
if (mService.mBackNavigationController.restoreBackNavigation()) {
effects |= TRANSACT_EFFECTS_LIFECYCLE;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 86adc1944371..1a107c24a16a 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -133,7 +133,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
private int mRapidActivityLaunchCount;
// all about the first app in the process
- final ApplicationInfo mInfo;
+ volatile ApplicationInfo mInfo;
final String mName;
final int mUid;
@@ -1805,12 +1805,17 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
Configuration overrideConfig = new Configuration(r.getRequestedOverrideConfiguration());
overrideConfig.assetsSeq = assetSeq;
r.onRequestedOverrideConfigurationChanged(overrideConfig);
+ r.updateApplicationInfo(mInfo);
if (r.isVisibleRequested()) {
r.ensureActivityConfiguration();
}
}
}
+ public void updateApplicationInfo(ApplicationInfo aInfo) {
+ mInfo = aInfo;
+ }
+
/**
* This is called for sending {@link android.app.servertransaction.LaunchActivityItem}.
* The caller must call {@link #setLastReportedConfiguration} if the delivered configuration
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7e450dd965d6..aca6f7235714 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -16815,6 +16815,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
EnforcingAdmin enforcingAdmin;
+
+ // TODO(b/370472975): enable when we stop policy enforecer callback from blocking the main
+ // thread
if (Flags.setPermissionGrantStateCoexistence()) {
enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
admin,
@@ -16840,6 +16843,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
callback.sendResult(null);
return;
}
+
+ // TODO(b/266924257): decide how to handle the internal state if the package doesn't
+ // exist, or the permission isn't requested by the app, because we could end up with
+ // inconsistent state between the policy engine and package manager. Also a package
+ // might get removed or has it's permission updated after we've set the policy.
+ if (grantState == PERMISSION_GRANT_STATE_DEFAULT) {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.PERMISSION_GRANT(packageName, permission),
+ enforcingAdmin,
+ caller.getUserId());
+ } else {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PERMISSION_GRANT(packageName, permission),
+ enforcingAdmin,
+ new IntegerPolicyValue(grantState),
+ caller.getUserId());
+ }
+ int newState = mInjector.binderWithCleanCallingIdentity(() ->
+ getPermissionGrantStateForUser(
+ packageName, permission, caller, caller.getUserId()));
+ if (newState == grantState) {
+ callback.sendResult(Bundle.EMPTY);
+ } else {
+ callback.sendResult(null);
+ }
} else {
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
@@ -16862,9 +16890,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
synchronized (getLockObject()) {
long ident = mInjector.binderClearCallingIdentity();
+ boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
+ >= android.os.Build.VERSION_CODES.Q;
+
try {
- boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
- >= android.os.Build.VERSION_CODES.Q;
if (!isPostQAdmin) {
// Legacy admins assume that they cannot control pre-M apps
if (getTargetSdk(packageName, caller.getUserId())
@@ -16877,47 +16906,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
callback.sendResult(null);
return;
}
- } catch (SecurityException e) {
- Slogf.e(LOG_TAG, "Could not set permission grant state", e);
- callback.sendResult(null);
- } finally {
- mInjector.binderRestoreCallingIdentity(ident);
- }
- }
- }
- // TODO(b/278710449): enable when we stop policy enforecer callback from blocking the main
- // thread
- if (false) {
- // TODO(b/266924257): decide how to handle the internal state if the package doesn't
- // exist, or the permission isn't requested by the app, because we could end up with
- // inconsistent state between the policy engine and package manager. Also a package
- // might get removed or has it's permission updated after we've set the policy.
- if (grantState == PERMISSION_GRANT_STATE_DEFAULT) {
- mDevicePolicyEngine.removeLocalPolicy(
- PolicyDefinition.PERMISSION_GRANT(packageName, permission),
- enforcingAdmin,
- caller.getUserId());
- } else {
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.PERMISSION_GRANT(packageName, permission),
- enforcingAdmin,
- new IntegerPolicyValue(grantState),
- caller.getUserId());
- }
- int newState = mInjector.binderWithCleanCallingIdentity(() ->
- getPermissionGrantStateForUser(
- packageName, permission, caller, caller.getUserId()));
- if (newState == grantState) {
- callback.sendResult(Bundle.EMPTY);
- } else {
- callback.sendResult(null);
- }
- } else {
- synchronized (getLockObject()) {
- long ident = mInjector.binderClearCallingIdentity();
- try {
- boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
- >= android.os.Build.VERSION_CODES.Q;
if (grantState == PERMISSION_GRANT_STATE_GRANTED
|| grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
|| grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) {
@@ -16939,7 +16927,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
} catch (SecurityException e) {
Slogf.e(LOG_TAG, "Could not set permission grant state", e);
-
callback.sendResult(null);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b9727f9f3970..4103c477a8ae 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -327,8 +327,6 @@ public final class SystemServer implements Dumpable {
* Implementation class names for services in the {@code SYSTEMSERVERCLASSPATH}
* from {@code PRODUCT_SYSTEM_SERVER_JARS} that are *not* in {@code services.jar}.
*/
- private static final String ARC_NETWORK_SERVICE_CLASS =
- "com.android.server.arc.net.ArcNetworkService";
private static final String ARC_PERSISTENT_DATA_BLOCK_SERVICE_CLASS =
"com.android.server.arc.persistent_data_block.ArcPersistentDataBlockService";
private static final String ARC_SYSTEM_HEALTH_SERVICE =
@@ -2101,24 +2099,13 @@ public final class SystemServer implements Dumpable {
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI)) {
// Wifi Service must be started first for wifi-related services.
- if (!isArc) {
- t.traceBegin("StartWifi");
- mSystemServiceManager.startServiceFromJar(
- WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
- t.traceEnd();
- t.traceBegin("StartWifiScanning");
- mSystemServiceManager.startServiceFromJar(
- WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
- t.traceEnd();
- }
- }
-
- // ARC - ArcNetworkService registers the ARC network stack and replaces the
- // stock WiFi service in both ARC++ container and ARCVM. Always starts the ARC network
- // stack regardless of whether FEATURE_WIFI is enabled/disabled (b/254755875).
- if (isArc) {
- t.traceBegin("StartArcNetworking");
- mSystemServiceManager.startService(ARC_NETWORK_SERVICE_CLASS);
+ t.traceBegin("StartWifi");
+ mSystemServiceManager.startServiceFromJar(
+ WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
+ t.traceEnd();
+ t.traceBegin("StartWifiScanning");
+ mSystemServiceManager.startServiceFromJar(
+ WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
t.traceEnd();
}
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index cc340c0a5f79..891c3349a43f 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -58,6 +58,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.util.EventLog;
import android.util.Log;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
@@ -1737,6 +1738,11 @@ public class MidiService extends IMidiManager.Stub {
pw.decreaseIndent();
}
+ @Override
+ protected void onUnhandledException(int code, int flags, Exception e) {
+ Slog.wtf(TAG, "Uncaught exception in AudioService: " + code + ", " + flags, e);
+ }
+
@GuardedBy("mUsbMidiLock")
private boolean isUsbMidiDeviceInUseLocked(MidiDeviceInfo info) {
String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME);
diff --git a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
index ff901af3defa..30df4c821134 100644
--- a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
@@ -96,6 +96,8 @@ class CallLogQueryHelper {
} catch (SecurityException ex) {
Slog.e(TAG, "Query call log failed: " + ex);
return false;
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception when querying call log.", e);
}
return hasResults;
}
diff --git a/services/people/java/com/android/server/people/data/ContactsQueryHelper.java b/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
index 2505abf2d160..2bd9d87b0124 100644
--- a/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/ContactsQueryHelper.java
@@ -151,9 +151,11 @@ class ContactsQueryHelper {
found = true;
}
} catch (SQLiteException exception) {
- Slog.w("SQLite exception when querying contacts.", exception);
+ Slog.w(TAG, "SQLite exception when querying contacts.", exception);
} catch (IllegalArgumentException exception) {
- Slog.w("Illegal Argument exception when querying contacts.", exception);
+ Slog.w(TAG, "Illegal Argument exception when querying contacts.", exception);
+ } catch (Exception exception) {
+ Slog.e(TAG, "Exception when querying contacts.", exception);
}
if (found && lookupKey != null && hasPhoneNumber) {
return queryPhoneNumber(lookupKey);
@@ -181,6 +183,8 @@ class ContactsQueryHelper {
mPhoneNumber = cursor.getString(phoneNumIdx);
}
}
+ } catch (Exception exception) {
+ Slog.e(TAG, "Exception when querying contact phone number.", exception);
}
return true;
}
diff --git a/services/people/java/com/android/server/people/data/MmsQueryHelper.java b/services/people/java/com/android/server/people/data/MmsQueryHelper.java
index 39dba9c73ba2..414a523fb186 100644
--- a/services/people/java/com/android/server/people/data/MmsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/MmsQueryHelper.java
@@ -100,6 +100,8 @@ class MmsQueryHelper {
}
}
}
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception when querying MMS table.", e);
} finally {
Binder.defaultBlockingForCurrentThread();
}
@@ -133,6 +135,8 @@ class MmsQueryHelper {
address = cursor.getString(addrIndex);
}
}
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception when querying MMS address table.", e);
}
if (!Mms.isPhoneNumber(address)) {
return null;
diff --git a/services/people/java/com/android/server/people/data/SmsQueryHelper.java b/services/people/java/com/android/server/people/data/SmsQueryHelper.java
index a5eb3a581616..f8ff3abc8e4c 100644
--- a/services/people/java/com/android/server/people/data/SmsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/SmsQueryHelper.java
@@ -98,6 +98,8 @@ class SmsQueryHelper {
}
}
}
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception when querying SMS table.", e);
} finally {
Binder.defaultBlockingForCurrentThread();
}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
index cbca434a6bb6..8af5b2081b81 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
@@ -21,6 +21,7 @@ import android.content.pm.PackageInstaller.SessionParams
import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DEFAULT
import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DENIED
import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED
+import android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED
import android.content.pm.PackageManager
import android.content.pm.verify.domain.DomainSet
import android.os.Parcel
@@ -33,6 +34,11 @@ import com.android.internal.os.BackgroundThread
import com.android.server.pm.verify.pkg.VerifierController
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
import libcore.io.IoUtils
import org.junit.Before
import org.junit.Rule
@@ -46,11 +52,6 @@ import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
-import java.io.File
-import java.io.FileInputStream
-import java.io.FileNotFoundException
-import java.io.FileOutputStream
-import java.io.IOException
@Presubmit
class PackageInstallerSessionTest {
@@ -197,7 +198,8 @@ class PackageInstallerSessionTest {
/* stagedSessionErrorCode */ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
/* stagedSessionErrorMessage */ "some error",
/* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")),
- /* VerifierController */ mock(VerifierController::class.java)
+ /* VerifierController */ mock(VerifierController::class.java),
+ VERIFICATION_POLICY_BLOCK_FAIL_CLOSED
)
}
@@ -339,6 +341,7 @@ class PackageInstallerSessionTest {
assertThat(expected.childSessionIds).asList()
.containsExactlyElementsIn(actual.childSessionIds.toList())
assertThat(expected.preVerifiedDomains).isEqualTo(actual.preVerifiedDomains)
+ assertThat(expected.verificationPolicy).isEqualTo(actual.verificationPolicy)
}
private fun assertInstallSourcesEquivalent(expected: InstallSource, actual: InstallSource) {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
index be094b0152bc..37b23b107ecd 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
@@ -16,6 +16,9 @@
package com.android.server.pm.verify.pkg;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_OPEN;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -92,6 +95,9 @@ public class VerifierControllerTest {
private static final long TEST_TIMEOUT_DURATION_MILLIS = TimeUnit.MINUTES.toMillis(1);
private static final long TEST_MAX_TIMEOUT_DURATION_MILLIS =
TimeUnit.MINUTES.toMillis(10);
+ private static final long TEST_VERIFIER_CONNECTION_TIMEOUT_DURATION_MILLIS =
+ TimeUnit.SECONDS.toMillis(10);
+ private static final int TEST_POLICY = VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
private final ArrayList<SharedLibraryInfo> mTestDeclaredLibraries = new ArrayList<>();
private final PersistableBundle mTestExtensionParams = new PersistableBundle();
@@ -124,6 +130,9 @@ public class VerifierControllerTest {
TEST_TIMEOUT_DURATION_MILLIS);
when(mInjector.getMaxVerificationExtendedTimeoutMillis()).thenReturn(
TEST_MAX_TIMEOUT_DURATION_MILLIS);
+ when(mInjector.getVerifierConnectionTimeoutMillis()).thenReturn(
+ TEST_VERIFIER_CONNECTION_TIMEOUT_DURATION_MILLIS
+ );
// Mock time forward as the code continues to check for the current time
when(mInjector.getCurrentTimeMillis())
.thenReturn(TEST_REQUEST_START_TIME)
@@ -159,12 +168,12 @@ public class VerifierControllerTest {
.isFalse();
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false)).isFalse();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false)).isFalse();
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ true)).isFalse();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ true)).isFalse();
verifyZeroInteractions(mSessionCallback);
}
@@ -200,8 +209,8 @@ public class VerifierControllerTest {
ServiceConnector.ServiceLifecycleCallbacks<IVerifierService> callbacks = captor.getValue();
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false)).isTrue();
verify(mMockService, times(1)).onVerificationRequired(any(VerificationSession.class));
callbacks.onBinderDied();
// Test that nothing crashes if the service connection is lost
@@ -212,12 +221,12 @@ public class VerifierControllerTest {
verifyNoMoreInteractions(mMockService);
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false)).isTrue();
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ true)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ true)).isTrue();
mVerifierController.notifyVerificationTimeout(TEST_ID);
verify(mMockService, times(1)).onVerificationTimeout(eq(TEST_ID));
}
@@ -226,8 +235,8 @@ public class VerifierControllerTest {
public void testNotifyPackageNameAvailable() throws Exception {
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false)).isTrue();
mVerifierController.notifyPackageNameAvailable(TEST_PACKAGE_NAME);
verify(mMockService).onPackageNameAvailable(eq(TEST_PACKAGE_NAME));
}
@@ -236,8 +245,8 @@ public class VerifierControllerTest {
public void testNotifyVerificationCancelled() throws Exception {
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false)).isTrue();
mVerifierController.notifyVerificationCancelled(TEST_PACKAGE_NAME);
verify(mMockService).onVerificationCancelled(eq(TEST_PACKAGE_NAME));
}
@@ -248,8 +257,8 @@ public class VerifierControllerTest {
ArgumentCaptor.forClass(VerificationSession.class);
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false)).isTrue();
verify(mMockService).onVerificationRequired(captor.capture());
VerificationSession session = captor.getValue();
assertThat(session.getId()).isEqualTo(TEST_ID);
@@ -276,8 +285,8 @@ public class VerifierControllerTest {
ArgumentCaptor.forClass(VerificationSession.class);
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ true)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ true)).isTrue();
verify(mMockService).onVerificationRetry(captor.capture());
VerificationSession session = captor.getValue();
assertThat(session.getId()).isEqualTo(TEST_ID);
@@ -302,8 +311,8 @@ public class VerifierControllerTest {
public void testNotifyVerificationTimeout() throws Exception {
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ true)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ true)).isTrue();
mVerifierController.notifyVerificationTimeout(TEST_ID);
verify(mMockService).onVerificationTimeout(eq(TEST_ID));
}
@@ -320,8 +329,8 @@ public class VerifierControllerTest {
ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false)).isTrue();
verify(mHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
verify(mSessionCallback, times(1)).onTimeout();
verify(mInjector, times(2)).getCurrentTimeMillis();
@@ -339,8 +348,8 @@ public class VerifierControllerTest {
.thenAnswer(i -> true);
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false)).isTrue();
verify(mHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
verify(mSessionCallback, times(1)).onTimeout();
verify(mInjector, times(2)).getCurrentTimeMillis();
@@ -350,8 +359,8 @@ public class VerifierControllerTest {
ArgumentCaptor.forClass(VerificationSession.class);
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ true)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ true)).isTrue();
verify(mMockService).onVerificationRetry(captor.capture());
VerificationSession session = captor.getValue();
VerificationStatus status = new VerificationStatus.Builder().setVerified(true).build();
@@ -367,8 +376,8 @@ public class VerifierControllerTest {
ArgumentCaptor.forClass(VerificationSession.class);
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false)).isTrue();
verify(mMockService).onVerificationRequired(captor.capture());
VerificationSession session = captor.getValue();
session.reportVerificationIncomplete(VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN);
@@ -383,8 +392,8 @@ public class VerifierControllerTest {
ArgumentCaptor.forClass(VerificationSession.class);
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false)).isTrue();
verify(mMockService).onVerificationRequired(captor.capture());
VerificationSession session = captor.getValue();
VerificationStatus status = new VerificationStatus.Builder().setVerified(true).build();
@@ -401,8 +410,8 @@ public class VerifierControllerTest {
ArgumentCaptor.forClass(VerificationSession.class);
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false)).isTrue();
verify(mMockService).onVerificationRequired(captor.capture());
VerificationSession session = captor.getValue();
VerificationStatus status = new VerificationStatus.Builder()
@@ -421,8 +430,8 @@ public class VerifierControllerTest {
ArgumentCaptor.forClass(VerificationSession.class);
assertThat(mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false)).isTrue();
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false)).isTrue();
verify(mMockService).onVerificationRequired(captor.capture());
VerificationSession session = captor.getValue();
VerificationStatus status = new VerificationStatus.Builder().setVerified(true).build();
@@ -439,8 +448,8 @@ public class VerifierControllerTest {
ArgumentCaptor.forClass(VerificationSession.class);
mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false);
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false);
verify(mMockService).onVerificationRequired(captor.capture());
VerificationSession session = captor.getValue();
final long initialTimeoutTime = TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS;
@@ -456,8 +465,8 @@ public class VerifierControllerTest {
ArgumentCaptor.forClass(VerificationSession.class);
mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false);
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false);
verify(mMockService).onVerificationRequired(captor.capture());
VerificationSession session = captor.getValue();
final long initialTimeoutTime = TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS;
@@ -493,10 +502,27 @@ public class VerifierControllerTest {
.thenReturn(TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS + 1);
mVerifierController.startVerificationSession(
mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
- TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
- /* retry= */ false);
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false);
verify(mHandler, times(3)).sendMessageAtTime(any(Message.class), anyLong());
verify(mInjector, times(6)).getCurrentTimeMillis();
verify(mSessionCallback, times(1)).onTimeout();
}
+
+ @Test
+ public void testPolicyOverride() throws Exception {
+ ArgumentCaptor<VerificationSession> captor =
+ ArgumentCaptor.forClass(VerificationSession.class);
+ mVerifierController.startVerificationSession(
+ mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
+ TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+ mSessionCallback, /* retry= */ false);
+ verify(mMockService).onVerificationRequired(captor.capture());
+ VerificationSession session = captor.getValue();
+ final int policy = VERIFICATION_POLICY_BLOCK_FAIL_OPEN;
+ when(mSessionCallback.setVerificationPolicy(eq(policy))).thenReturn(true);
+ assertThat(session.setVerificationPolicy(policy)).isTrue();
+ assertThat(session.getVerificationPolicy()).isEqualTo(policy);
+ verify(mSessionCallback, times(1)).setVerificationPolicy(eq(policy));
+ }
}
diff --git a/services/tests/appfunctions/Android.bp b/services/tests/appfunctions/Android.bp
index c841643c6654..836f90b992d6 100644
--- a/services/tests/appfunctions/Android.bp
+++ b/services/tests/appfunctions/Android.bp
@@ -36,7 +36,9 @@ android_test {
"androidx.test.core",
"androidx.test.runner",
"androidx.test.ext.truth",
+ "androidx.core_core-ktx",
"kotlin-test",
+ "kotlinx_coroutines_test",
"platform-test-annotations",
"services.appfunctions",
"servicestests-core-utils",
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt
new file mode 100644
index 000000000000..a69e9025bfa0
--- /dev/null
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.appfunctions
+
+import android.app.appfunctions.flags.Flags
+import android.content.Context
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
+class AppFunctionManagerServiceImplTest {
+ @get:Rule
+ val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ private val context: Context
+ get() = ApplicationProvider.getApplicationContext()
+
+ private val serviceImpl = AppFunctionManagerServiceImpl(context)
+
+ @Test
+ fun testGetLockForPackage_samePackage() {
+ val packageName = "com.example.app"
+ val lock1 = serviceImpl.getLockForPackage(packageName)
+ val lock2 = serviceImpl.getLockForPackage(packageName)
+
+ // Assert that the same lock object is returned for the same package name
+ assertThat(lock1).isEqualTo(lock2)
+ }
+
+ @Test
+ fun testGetLockForPackage_differentPackages() {
+ val packageName1 = "com.example.app1"
+ val packageName2 = "com.example.app2"
+ val lock1 = serviceImpl.getLockForPackage(packageName1)
+ val lock2 = serviceImpl.getLockForPackage(packageName2)
+
+ // Assert that different lock objects are returned for different package names
+ assertThat(lock1).isNotEqualTo(lock2)
+ }
+
+ @Ignore("Hard to deterministically trigger the garbage collector.")
+ @Test
+ fun testWeakReference_garbageCollected_differentLockAfterGC() = runTest {
+ // Create a large number of temporary objects to put pressure on the GC
+ val tempObjects = MutableList<Any?>(10000000) { Any() }
+ var callingPackage: String? = "com.example.app"
+ var lock1: Any? = serviceImpl.getLockForPackage(callingPackage)
+ callingPackage = null // Set the key to null
+ val lock1Hash = lock1.hashCode()
+ lock1 = null
+
+ // Create memory pressure
+ repeat(3) {
+ for (i in 1..100) {
+ "a".repeat(10000)
+ }
+ System.gc() // Suggest garbage collection
+ System.runFinalization()
+ }
+ // Get the lock again - it should be a different object now
+ val lock2 = serviceImpl.getLockForPackage("com.example.app")
+ // Assert that the lock objects are different
+ assertThat(lock1Hash).isNotEqualTo(lock2.hashCode())
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 3b5a386dd303..b917af4d796e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -1658,6 +1658,70 @@ public class DisplayManagerServiceTest {
}
@Test
+ public void testGetDisplayIdsByGroupsIds() throws Exception {
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ // Create display 1
+ FakeDisplayDevice displayDevice1 =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ LogicalDisplay display1 = logicalDisplayMapper.getDisplayLocked(displayDevice1);
+ final int groupId1 = display1.getDisplayInfoLocked().displayGroupId;
+ // Create display 2
+ FakeDisplayDevice displayDevice2 =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ LogicalDisplay display2 = logicalDisplayMapper.getDisplayLocked(displayDevice2);
+ final int groupId2 = display2.getDisplayInfoLocked().displayGroupId;
+ // Both displays should be in the same display group
+ assertEquals(groupId1, groupId2);
+ final int[] displayIds = new int[]{
+ display1.getDisplayIdLocked(), display2.getDisplayIdLocked()};
+ final SparseArray<int[]> expectedDisplayGroups = new SparseArray<>();
+ expectedDisplayGroups.put(groupId1, displayIds);
+
+ final SparseArray<int[]> displayGroups = localService.getDisplayIdsByGroupsIds();
+
+ for (int i = 0; i < expectedDisplayGroups.size(); i++) {
+ final int groupId = expectedDisplayGroups.keyAt(i);
+ assertTrue(displayGroups.contains(groupId));
+ assertArrayEquals(expectedDisplayGroups.get(groupId), displayGroups.get(groupId));
+ }
+ }
+
+ @Test
+ public void testGetDisplayIdsByGroupsIds_multipleDisplayGroups() throws Exception {
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ // Create display 1
+ FakeDisplayDevice displayDevice1 =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ LogicalDisplay display1 = logicalDisplayMapper.getDisplayLocked(displayDevice1);
+ final int groupId1 = display1.getDisplayInfoLocked().displayGroupId;
+ // Create display 2
+ FakeDisplayDevice displayDevice2 =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+ LogicalDisplay display2 = logicalDisplayMapper.getDisplayLocked(displayDevice2);
+ final int groupId2 = display2.getDisplayInfoLocked().displayGroupId;
+ // Both displays should be in different display groups
+ assertNotEquals(groupId1, groupId2);
+ final SparseArray<int[]> expectedDisplayGroups = new SparseArray<>();
+ expectedDisplayGroups.put(groupId1, new int[]{display1.getDisplayIdLocked()});
+ expectedDisplayGroups.put(groupId2, new int[]{display2.getDisplayIdLocked()});
+
+ final SparseArray<int[]> displayGroups = localService.getDisplayIdsByGroupsIds();
+
+ assertEquals(expectedDisplayGroups.size(), displayGroups.size());
+ for (int i = 0; i < expectedDisplayGroups.size(); i++) {
+ final int groupId = expectedDisplayGroups.keyAt(i);
+ assertTrue(displayGroups.contains(groupId));
+ assertArrayEquals(expectedDisplayGroups.get(groupId), displayGroups.get(groupId));
+ }
+ }
+
+ @Test
public void testCreateVirtualDisplay_isValidProjection_notValid()
throws RemoteException {
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 01b2d3e34bdc..fdf6b809fa85 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -2343,6 +2343,41 @@ public final class DisplayPowerControllerTest {
}
}
+ @Test
+ public void stylusUsageStarted_disablesAutomaticBrightnessStrategy() {
+ when(mDisplayManagerFlagsMock.isBlockAutobrightnessChangesOnStylusUsage())
+ .thenReturn(true);
+ when(mDisplayManagerFlagsMock.isRefactorDisplayPowerControllerEnabled())
+ .thenReturn(true);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+ advanceTime(2);
+ clearInvocations(mHolder.automaticBrightnessController);
+ mHolder.dpc.stylusGestureStarted(2000000);
+
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+ verify(mHolder.automaticBrightnessController, times(0))
+ .getAutomaticScreenBrightness(any());
+
+ // Stylus usage timed out, hence autobrightness is now enabled back again
+ advanceTime(6);
+ verify(mHolder.automaticBrightnessController).getAutomaticScreenBrightness(null);
+
+ // Ideally we should be able to assert against new BrightnessEvent(Display.DEFAULT_DISPLAY),
+ // but because brightnessEvent has the mTime field which refers to the current time,
+ // asserting against that is non-trivial
+ verify(mHolder.automaticBrightnessController).getAutomaticScreenBrightness(
+ any(BrightnessEvent.class));
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
@@ -2406,6 +2441,7 @@ public final class DisplayPowerControllerTest {
.thenReturn(new int[0]);
when(displayDeviceConfigMock.getDefaultDozeBrightness())
.thenReturn(DEFAULT_DOZE_BRIGHTNESS);
+ when(displayDeviceConfigMock.getIdleStylusTimeoutMillis()).thenReturn(5);
when(displayDeviceConfigMock.getBrightnessRampFastDecrease())
.thenReturn(BRIGHTNESS_RAMP_RATE_FAST_DECREASE);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
index 04b79b4f1761..d93ee84f4870 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
@@ -74,7 +74,7 @@ public final class BrightnessReasonTest {
@Test
public void setModifierDoesntSetIfModifierIsBeyondExtremes() {
- int extremeModifier = 0x40; // equal to BrightnessReason.MODIFIER_MASK * 2
+ int extremeModifier = 0x80;
// reset modifier
mBrightnessReason.setModifier(0);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index c0698756a3d7..b3baa5deb4a7 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -125,7 +125,7 @@ public final class DisplayBrightnessControllerTest {
DisplayManagerInternal.DisplayOffloadSession.class));
verify(displayBrightnessStrategy).updateBrightness(
eq(new StrategyExecutionRequest(displayPowerRequest, DEFAULT_BRIGHTNESS,
- /* userSetBrightnessChanged= */ false)));
+ /* userSetBrightnessChanged= */ false, /* isStylusBeingUsed */ false)));
assertEquals(mDisplayBrightnessController.getCurrentDisplayBrightnessStrategy(),
displayBrightnessStrategy);
}
@@ -559,4 +559,11 @@ public final class DisplayBrightnessControllerTest {
displayDeviceConfig, handler, brightnessMappingStrategy, isDisplayEnabled,
leadDisplayId);
}
+
+ @Test
+ public void setStylusBeingUsed_setsStylusInUseState() {
+ assertFalse(mDisplayBrightnessController.isStylusBeingUsed());
+ mDisplayBrightnessController.setStylusBeingUsed(true);
+ assertTrue(mDisplayBrightnessController.isStylusBeingUsed());
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index a44c517ed9cf..fe1505162e24 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -68,6 +68,8 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public final class DisplayBrightnessStrategySelectorTest {
private static final boolean DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING = false;
+ private static final boolean STYLUS_IS_NOT_BEING_USED = false;
+ private static final boolean STYLUS_IS_BEING_USED = true;
private static final int DISPLAY_ID = 1;
@Mock
@@ -196,7 +198,8 @@ public final class DisplayBrightnessStrategySelectorTest {
DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mDozeBrightnessModeStrategy);
}
@@ -212,7 +215,8 @@ public final class DisplayBrightnessStrategySelectorTest {
DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING);
assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mDozeBrightnessModeStrategy);
}
@@ -226,7 +230,8 @@ public final class DisplayBrightnessStrategySelectorTest {
DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING);
assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mDozeBrightnessModeStrategy);
}
@@ -249,7 +254,8 @@ public final class DisplayBrightnessStrategySelectorTest {
assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mDozeBrightnessModeStrategy);
}
@@ -259,7 +265,8 @@ public final class DisplayBrightnessStrategySelectorTest {
DisplayManagerInternal.DisplayPowerRequest.class);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_OFF,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mScreenOffBrightnessModeStrategy);
}
@@ -271,7 +278,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mOverrideBrightnessStrategy);
}
@@ -284,7 +292,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(0.3f);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mTemporaryBrightnessStrategy);
}
@@ -298,7 +307,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mBoostBrightnessStrategy);
}
@@ -312,7 +322,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(Float.NaN);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mInvalidBrightnessStrategy);
}
@@ -323,7 +334,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(0.3f);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mFollowerBrightnessStrategy);
}
@@ -341,7 +353,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(0.3f);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mOffloadBrightnessStrategy);
}
@@ -365,7 +378,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(true);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mAutomaticBrightnessStrategy);
verify(mAutomaticBrightnessStrategy).setAutoBrightnessState(Display.STATE_ON,
true, BrightnessReason.REASON_UNKNOWN,
@@ -373,6 +387,32 @@ public final class DisplayBrightnessStrategySelectorTest {
/* useNormalBrightnessForDoze= */ false, 0.1f, false);
}
+
+ @Test
+ public void selectStrategy_doesNotSelectAutomaticStrategyWhenStylusInUse() {
+ when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
+ when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true);
+ when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true);
+ when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
+ true);
+ mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
+ mInjector, DISPLAY_ID, mDisplayManagerFlags);
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+ DisplayManagerInternal.DisplayPowerRequest.class);
+ displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+ displayPowerRequest.screenBrightnessOverride = Float.NaN;
+ when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
+ when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
+ when(mAutomaticBrightnessStrategy.shouldUseAutoBrightness()).thenReturn(true);
+ when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(true);
+ assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
+ new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_BEING_USED)),
+ mAutomaticBrightnessStrategy);
+ }
+
@Test
public void selectStrategy_selectsAutomaticFallbackStrategyWhenValid() {
when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
@@ -389,7 +429,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mAutoBrightnessFallbackStrategy.isValid()).thenReturn(true);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mAutoBrightnessFallbackStrategy);
}
@@ -407,7 +448,8 @@ public final class DisplayBrightnessStrategySelectorTest {
assertNotEquals(mOffloadBrightnessStrategy,
mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)));
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)));
}
@Test
@@ -425,7 +467,8 @@ public final class DisplayBrightnessStrategySelectorTest {
when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(false);
assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession)),
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED)),
mFallbackBrightnessStrategy);
}
@@ -440,7 +483,8 @@ public final class DisplayBrightnessStrategySelectorTest {
mDisplayBrightnessStrategySelector.selectStrategy(
new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
- 0.1f, false, mDisplayOffloadSession));
+ 0.1f, false, mDisplayOffloadSession,
+ STYLUS_IS_NOT_BEING_USED));
StrategySelectionNotifyRequest strategySelectionNotifyRequest =
new StrategySelectionNotifyRequest(displayPowerRequest, Display.STATE_ON,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
index 99dfa739fb80..2a71af06e0c2 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
@@ -129,7 +129,8 @@ public class AutoBrightnessFallbackStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mAutoBrightnessFallbackStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index efa8b3ef775f..8a1f86093ecf 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -637,7 +637,7 @@ public class AutomaticBrightnessStrategyTest {
.build();
DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
.updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
- /* userSetBrightnessChanged= */ true));
+ /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false));
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
@@ -686,7 +686,7 @@ public class AutomaticBrightnessStrategyTest {
.build();
DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
.updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
- /* userSetBrightnessChanged= */ true));
+ /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false));
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
@@ -725,7 +725,7 @@ public class AutomaticBrightnessStrategyTest {
.build();
DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
.updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
- /* userSetBrightnessChanged= */ true));
+ /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false));
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
@@ -764,7 +764,7 @@ public class AutomaticBrightnessStrategyTest {
.build();
DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
.updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
- /* userSetBrightnessChanged= */ true));
+ /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false));
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
index 275bb3efee8e..c03309e8b4a4 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
@@ -60,7 +60,8 @@ public class BoostBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mBoostBrightnessStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
index 23e447c25245..e7f80b04e669 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
@@ -57,7 +57,8 @@ public class DozeBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mDozeBrightnessModeStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
index c4a579092d38..dcfa174a53f5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
@@ -61,7 +61,8 @@ public class FallbackBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mFallbackBrightnessStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, currentBrightness,
- /* userSetBrightnessChanged= */ true));
+ /* userSetBrightnessChanged= */ true,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
index c01f96e800de..239cdb6002e9 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
@@ -61,7 +61,8 @@ public class FollowerBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mFollowerBrightnessStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(expectedDisplayBrightnessState, updatedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
index 9fb2afa26ed2..77302f8747c1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
@@ -72,7 +72,8 @@ public class OffloadBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mOffloadBrightnessStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT, mOffloadBrightnessStrategy
.getOffloadScreenBrightness(), 0.0f);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
index e8b4c06b9c89..cc21af19722b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
@@ -60,7 +60,8 @@ public class OverrideBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mOverrideBrightnessStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
index 38709ece7007..652663e52a0a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
@@ -58,7 +58,8 @@ public final class ScreenOffBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mScreenOffBrightnessModeStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
index f523b6af426b..0022cab371e8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
@@ -60,7 +60,8 @@ public class TemporaryBrightnessStrategyTest {
DisplayBrightnessState updatedDisplayBrightnessState =
mTemporaryBrightnessStrategy.updateBrightness(
new StrategyExecutionRequest(displayPowerRequest, 0.2f,
- /* userSetBrightnessChanged= */ false));
+ /* userSetBrightnessChanged= */ false,
+ /* isStylusBeingUsed */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
index 121145672d68..439243e85e75 100644
--- a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
+++ b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
@@ -82,6 +82,11 @@ public class AudioManagerRouteControllerTest {
private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET =
createAudioDeviceInfo(
AudioSystem.DEVICE_OUT_WIRED_HEADSET, "name_wired_hs", /* address= */ null);
+ private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS =
+ createAudioDeviceInfo(
+ AudioSystem.DEVICE_OUT_WIRED_HEADSET,
+ "name_wired_hs_with_address",
+ /* address= */ "card=1;device=0");
private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP =
createAudioDeviceInfo(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "name_a2dp", /* address= */ "12:34:45");
@@ -304,6 +309,55 @@ public class AudioManagerRouteControllerTest {
assertThat(selectedRoute.getName().toString()).isEqualTo(FAKE_ROUTE_NAME);
}
+ @Test
+ public void getAvailableRoutes_whenAddressIsPopulatedForNonBluetoothDevice_usesCorrectName() {
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS,
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS,
+ FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP);
+
+ List<MediaRoute2Info> availableRoutes = mControllerUnderTest.getAvailableRoutes();
+ assertThat(availableRoutes.size()).isEqualTo(3);
+
+ assertThat(
+ getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET)
+ .getName()
+ .toString())
+ .isEqualTo(
+ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS
+ .getProductName()
+ .toString());
+
+ assertThat(
+ getAvailableRouteWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP)
+ .getName()
+ .toString())
+ .isEqualTo(FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP.getProductName().toString());
+ }
+
+ @Test
+ public void
+ getAvailableRoutes_whenAddressIsNotPopulatedForNonBluetoothDevice_usesCorrectName() {
+ addAvailableAudioDeviceInfo(
+ /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET,
+ /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET);
+
+ List<MediaRoute2Info> availableRoutes = mControllerUnderTest.getAvailableRoutes();
+ assertThat(availableRoutes.size()).isEqualTo(2);
+
+ assertThat(
+ getAvailableRouteWithType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER)
+ .getName()
+ .toString())
+ .isEqualTo(FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER.getProductName().toString());
+
+ assertThat(
+ getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET)
+ .getName()
+ .toString())
+ .isEqualTo(FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET.getProductName().toString());
+ }
+
// Internal methods.
@NonNull
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 6ccc03709b4f..2a825f35bf62 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -107,6 +107,7 @@ import android.os.IBinder;
import android.os.IProgressListener;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -1309,12 +1310,13 @@ public class ActivityManagerServiceTest {
intent.putExtra("EXTRA_INTENT0", extraIntent);
intent.collectExtraIntentKeys();
- mAms.addCreatorToken(intent);
+ mAms.addCreatorToken(intent, TEST_PACKAGE);
ActivityManagerService.IntentCreatorToken token =
(ActivityManagerService.IntentCreatorToken) extraIntent.getCreatorToken();
assertThat(token).isNotNull();
assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid());
+ assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE);
}
@Test
@@ -1330,7 +1332,7 @@ public class ActivityManagerServiceTest {
fillinIntent.collectExtraIntentKeys();
intent.fillIn(fillinIntent, FILL_IN_ACTION);
- mAms.addCreatorToken(fillinIntent);
+ mAms.addCreatorToken(fillinIntent, TEST_PACKAGE);
fillinExtraIntent = intent.getParcelableExtra("FILLIN_EXTRA_INTENT0", Intent.class);
@@ -1338,6 +1340,49 @@ public class ActivityManagerServiceTest {
(ActivityManagerService.IntentCreatorToken) fillinExtraIntent.getCreatorToken();
assertThat(token).isNotNull();
assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid());
+ assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_PREVENT_INTENT_REDIRECT)
+ public void testCheckCreatorToken() {
+ Intent intent = new Intent();
+ Intent extraIntent = new Intent("EXTRA_INTENT_ACTION");
+ intent.putExtra("EXTRA_INTENT", extraIntent);
+
+ intent.collectExtraIntentKeys();
+
+ // mimic client hack and sneak in an extra intent without going thru collectExtraIntentKeys.
+ Intent extraIntent2 = new Intent("EXTRA_INTENT_ACTION2");
+ intent.putExtra("EXTRA_INTENT2", extraIntent2);
+
+ // mock parceling on the client side, unparcling on the system server side, then
+ // addCreatorToken on system server side.
+ final Parcel parcel = Parcel.obtain();
+ intent.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ Intent newIntent = new Intent();
+ newIntent.readFromParcel(parcel);
+ intent = newIntent;
+ mAms.addCreatorToken(intent, TEST_PACKAGE);
+ // entering the target app's process.
+ intent.checkCreatorToken();
+
+ Intent extraIntent3 = new Intent("EXTRA_INTENT_ACTION3");
+ intent.putExtra("EXTRA_INTENT3", extraIntent3);
+
+ extraIntent = intent.getParcelableExtra("EXTRA_INTENT", Intent.class);
+ extraIntent2 = intent.getParcelableExtra("EXTRA_INTENT2", Intent.class);
+ extraIntent3 = intent.getParcelableExtra("EXTRA_INTENT3", Intent.class);
+
+ assertThat(extraIntent.getExtendedFlags()
+ & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0);
+ // sneaked in intent should have EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN set.
+ assertThat(extraIntent2.getExtendedFlags()
+ & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isNotEqualTo(0);
+ // local created intent should not have EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN set.
+ assertThat(extraIntent3.getExtendedFlags()
+ & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0);
}
private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 31157f9e27dc..5c718d982476 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -25,7 +25,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.job.Flags.FLAG_COUNT_QUOTA_FIX;
-import static com.android.server.job.Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS;
import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX;
import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
@@ -77,9 +76,12 @@ import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.SparseBooleanArray;
@@ -90,12 +92,12 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.PowerAllowlistInternal;
+import com.android.server.job.Flags;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobStore;
import com.android.server.job.controllers.QuotaController.ExecutionStats;
import com.android.server.job.controllers.QuotaController.QcConstants;
-import com.android.server.job.controllers.QuotaController.QuotaBump;
import com.android.server.job.controllers.QuotaController.ShrinkableDebits;
import com.android.server.job.controllers.QuotaController.TimedEvent;
import com.android.server.job.controllers.QuotaController.TimingSession;
@@ -132,11 +134,11 @@ public class QuotaControllerTest {
private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
private static final int SOURCE_USER_ID = 0;
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private QuotaController mQuotaController;
private QuotaController.QcConstants mQcConstants;
private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants();
private int mSourceUid;
- private AppStandbyInternal.AppIdleStateChangeListener mAppIdleStateChangeListener;
private PowerAllowlistInternal.TempAllowlistChangeListener mTempAllowlistListener;
private IUidObserver mUidObserver;
private UsageStatsManagerInternal.UsageEventListener mUsageEventListener;
@@ -191,8 +193,7 @@ public class QuotaControllerTest {
when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
doReturn(mActivityMangerInternal)
.when(() -> LocalServices.getService(ActivityManagerInternal.class));
- final AppStandbyInternal appStandbyInternal = mock(AppStandbyInternal.class);
- doReturn(appStandbyInternal)
+ doReturn(mock(AppStandbyInternal.class))
.when(() -> LocalServices.getService(AppStandbyInternal.class));
doReturn(mock(BatteryManagerInternal.class))
.when(() -> LocalServices.getService(BatteryManagerInternal.class));
@@ -239,8 +240,6 @@ public class QuotaControllerTest {
// Initialize real objects.
// Capture the listeners.
- ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> aiscListenerCaptor =
- ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class);
ArgumentCaptor<IUidObserver> uidObserverCaptor =
ArgumentCaptor.forClass(IUidObserver.class);
ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor =
@@ -250,8 +249,6 @@ public class QuotaControllerTest {
mQuotaController = new QuotaController(mJobSchedulerService,
mock(BackgroundJobsController.class), mock(ConnectivityController.class));
- verify(appStandbyInternal).addListener(aiscListenerCaptor.capture());
- mAppIdleStateChangeListener = aiscListenerCaptor.getValue();
verify(mPowerAllowlistInternal)
.registerTempAllowlistChangeListener(taChangeCaptor.capture());
mTempAllowlistListener = taChangeCaptor.getValue();
@@ -488,14 +485,12 @@ public class QuotaControllerTest {
now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3);
TimingSession two = createTimingSession(
now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
- QuotaBump bump1 = new QuotaBump(now - 2 * HOUR_IN_MILLIS);
TimingSession thr = createTimingSession(
now - (3 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
// Overlaps 24 hour boundary.
TimingSession fou = createTimingSession(
now - (24 * HOUR_IN_MILLIS + 2 * MINUTE_IN_MILLIS), 7 * MINUTE_IN_MILLIS, 1);
// Way past the 24 hour boundary.
- QuotaBump bump2 = new QuotaBump(now - 24 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS);
TimingSession fiv = createTimingSession(
now - (25 * HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 4);
List<TimedEvent> expectedRegular = new ArrayList<>();
@@ -503,16 +498,13 @@ public class QuotaControllerTest {
// Added in correct (chronological) order.
expectedRegular.add(fou);
expectedRegular.add(thr);
- expectedRegular.add(bump1);
expectedRegular.add(two);
expectedRegular.add(one);
expectedEJ.add(fou);
expectedEJ.add(one);
mQuotaController.saveTimingSession(0, "com.android.test", fiv, false);
- mQuotaController.getTimingSessions(0, "com.android.test").add(bump2);
mQuotaController.saveTimingSession(0, "com.android.test", fou, false);
mQuotaController.saveTimingSession(0, "com.android.test", thr, false);
- mQuotaController.getTimingSessions(0, "com.android.test").add(bump1);
mQuotaController.saveTimingSession(0, "com.android.test", two, false);
mQuotaController.saveTimingSession(0, "com.android.test", one, false);
mQuotaController.saveTimingSession(0, "com.android.test", fiv, true);
@@ -942,7 +934,8 @@ public class QuotaControllerTest {
* Tests that getExecutionStatsLocked returns the correct stats.
*/
@Test
- public void testGetExecutionStatsLocked_Values() {
+ @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetExecutionStatsLocked_Values_LegacyDefaultBucketWindowSizes() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(now - (mQcConstants.WINDOW_SIZE_RARE_MS - HOUR_IN_MILLIS),
@@ -1027,11 +1020,112 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetExecutionStatsLocked_Values_NewDefaultBucketWindowSizes() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+
+ ExecutionStats expectedStats = new ExecutionStats();
+
+ // Exempted
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_EXEMPTED;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_EXEMPTED;
+ expectedStats.expirationTimeElapsed = now + 14 * MINUTE_IN_MILLIS;
+ expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInWindow = 5;
+ expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInMaxPeriod = 20;
+ expectedStats.sessionCountInWindow = 1;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ EXEMPTED_INDEX));
+ }
+
+ // Active
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+ // There is only one session in the past active bucket window, the empty time for this
+ // window is the bucket window size - duration of the session.
+ expectedStats.expirationTimeElapsed = now + 24 * MINUTE_IN_MILLIS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ ACTIVE_INDEX));
+ }
+
+ // Working
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_WORKING_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING;
+ expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+ expectedStats.executionTimeInWindowMs = 13 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInWindow = 10;
+ expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInMaxPeriod = 20;
+ expectedStats.sessionCountInWindow = 2;
+ expectedStats.inQuotaTimeElapsed = now + 2 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS
+ + mQcConstants.IN_QUOTA_BUFFER_MS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ WORKING_INDEX));
+ }
+
+ // Frequent
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_FREQUENT_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT;
+ expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+ expectedStats.executionTimeInWindowMs = 23 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInWindow = 15;
+ expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInMaxPeriod = 20;
+ expectedStats.sessionCountInWindow = 3;
+ expectedStats.inQuotaTimeElapsed = now + 10 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS
+ + mQcConstants.IN_QUOTA_BUFFER_MS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX));
+ }
+
+ // Rare
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_RARE_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
+ expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+ expectedStats.executionTimeInWindowMs = 33 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInWindow = 20;
+ expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInMaxPeriod = 20;
+ expectedStats.sessionCountInWindow = 4;
+ expectedStats.inQuotaTimeElapsed = now + 22 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS
+ + mQcConstants.IN_QUOTA_BUFFER_MS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ RARE_INDEX));
+ }
+ }
+
/**
* Tests that getExecutionStatsLocked returns the correct stats soon after device startup.
*/
@Test
- public void testGetExecutionStatsLocked_Values_BeginningOfTime() {
+ @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetExecutionStatsLocked_Values_BeginningOfTime_LegacyDefaultBucketWindowSizes() {
// Set time to 3 minutes after boot.
advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
advanceElapsedClock(3 * MINUTE_IN_MILLIS);
@@ -1054,7 +1148,8 @@ public class QuotaControllerTest {
expectedStats.sessionCountInWindow = 1;
synchronized (mQuotaController.mLock) {
assertEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test", ACTIVE_INDEX));
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ ACTIVE_INDEX));
}
// Working
@@ -1064,7 +1159,8 @@ public class QuotaControllerTest {
expectedStats.expirationTimeElapsed = 2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
synchronized (mQuotaController.mLock) {
assertEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test", WORKING_INDEX));
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ WORKING_INDEX));
}
// Frequent
@@ -1085,7 +1181,83 @@ public class QuotaControllerTest {
expectedStats.expirationTimeElapsed = 24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
synchronized (mQuotaController.mLock) {
assertEquals(expectedStats,
- mQuotaController.getExecutionStatsLocked(0, "com.android.test", RARE_INDEX));
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ RARE_INDEX));
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetExecutionStatsLocked_Values_BeginningOfTime_NewDefaultBucketWindowSizes() {
+ // Set time to 3 minutes after boot.
+ advanceElapsedClock(-JobSchedulerService.sElapsedRealtimeClock.millis());
+ advanceElapsedClock(3 * MINUTE_IN_MILLIS);
+
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 2), false);
+
+ ExecutionStats expectedStats = new ExecutionStats();
+
+ // Exempted
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_EXEMPTED_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+ expectedStats.expirationTimeElapsed = 10 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+ expectedStats.executionTimeInWindowMs = MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInWindow = 2;
+ expectedStats.executionTimeInMaxPeriodMs = MINUTE_IN_MILLIS;
+ expectedStats.bgJobCountInMaxPeriod = 2;
+ expectedStats.sessionCountInWindow = 1;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ EXEMPTED_INDEX));
+ }
+
+ // Active
+ expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
+ expectedStats.expirationTimeElapsed = 20 * MINUTE_IN_MILLIS + 11 * MINUTE_IN_MILLIS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ ACTIVE_INDEX));
+ }
+
+ // Working
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_WORKING_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING;
+ expectedStats.expirationTimeElapsed = 4 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ WORKING_INDEX));
+ }
+
+ // Frequent
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_FREQUENT_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT;
+ expectedStats.expirationTimeElapsed = 12 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX));
+ }
+
+ // Rare
+ expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_RARE_MS;
+ expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
+ expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
+ expectedStats.expirationTimeElapsed = 24 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
+ synchronized (mQuotaController.mLock) {
+ assertEquals(expectedStats,
+ mQuotaController.getExecutionStatsLocked(0, "com.android.test",
+ RARE_INDEX));
}
}
@@ -1093,7 +1265,8 @@ public class QuotaControllerTest {
* Tests that getExecutionStatsLocked returns the correct timing session stats when coalescing.
*/
@Test
- public void testGetExecutionStatsLocked_CoalescingSessions() {
+ @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetExecutionStatsLocked_CoalescingSessions_LegacyDefaultBucketWindowSizes() {
for (int i = 0; i < 10; ++i) {
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(
@@ -1110,12 +1283,14 @@ public class QuotaControllerTest {
advanceElapsedClock(54 * SECOND_IN_MILLIS);
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(
- JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false);
+ JobSchedulerService.sElapsedRealtimeClock.millis(),
+ 500, 1), false);
advanceElapsedClock(500);
advanceElapsedClock(400);
mQuotaController.saveTimingSession(0, "com.android.test",
createTimingSession(
- JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false);
+ JobSchedulerService.sElapsedRealtimeClock.millis(),
+ 100, 1), false);
advanceElapsedClock(100);
advanceElapsedClock(5 * SECOND_IN_MILLIS);
}
@@ -1241,6 +1416,164 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetExecutionStatsLocked_CoalescingSessions_NewDefaultBucketWindowSizes() {
+ for (int i = 0; i < 20; ++i) {
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(),
+ 5 * MINUTE_IN_MILLIS, 5), false);
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+ advanceElapsedClock(5 * MINUTE_IN_MILLIS);
+ for (int j = 0; j < 5; ++j) {
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(),
+ MINUTE_IN_MILLIS, 2), false);
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+ advanceElapsedClock(54 * SECOND_IN_MILLIS);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(), 500, 1), false);
+ advanceElapsedClock(500);
+ advanceElapsedClock(400);
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(
+ JobSchedulerService.sElapsedRealtimeClock.millis(), 100, 1), false);
+ advanceElapsedClock(100);
+ advanceElapsedClock(5 * SECOND_IN_MILLIS);
+ }
+ advanceElapsedClock(40 * MINUTE_IN_MILLIS);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 0);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(64, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(192, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(320, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 500);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_WORKING_MS * 5 TimingSessions are coalesced
+ assertEquals(44, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_FREQUENT_MS * 5 TimingSessions are coalesced
+ assertEquals(132, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_RARE_MS * 5 TimingSessions are coalesced
+ assertEquals(220, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, 1000);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(44, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(132, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(220, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 5 * SECOND_IN_MILLIS);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_WORKING_MS * 9 TimingSessions are coalesced
+ assertEquals(28, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_FREQUENT_MS * 9 TimingSessions are coalesced
+ assertEquals(84, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ // WINDOW_SIZE_RARE_MS * 9 TimingSessions are coalesced
+ assertEquals(140, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ MINUTE_IN_MILLIS);
+
+ // Only two TimingSessions there for every hour.
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(8, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(24, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(40, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 5 * MINUTE_IN_MILLIS);
+
+ // Only one TimingSessions there for every hour
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(4, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(12, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(20, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
+ 15 * MINUTE_IN_MILLIS);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(4, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(12, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(20, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+
+ // QuotaController caps the duration at 15 minutes, so there shouldn't be any difference
+ // between an hour and 15 minutes.
+ setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, HOUR_IN_MILLIS);
+
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.invalidateAllExecutionStatsLocked();
+ assertEquals(0, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", ACTIVE_INDEX).sessionCountInWindow);
+ assertEquals(4, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", WORKING_INDEX).sessionCountInWindow);
+ assertEquals(12, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", FREQUENT_INDEX).sessionCountInWindow);
+ assertEquals(20, mQuotaController.getExecutionStatsLocked(
+ 0, "com.android.test", RARE_INDEX).sessionCountInWindow);
+ }
+ }
+
/**
* Tests that getExecutionStatsLocked properly caches the stats and returns the cached object.
*/
@@ -1466,7 +1799,8 @@ public class QuotaControllerTest {
}
@Test
- public void testGetMaxJobExecutionTimeLocked_EJ() {
+ @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetMaxJobExecutionTimeLocked_EJ_LegacyDefaultEJLimits() {
final long timeUsedMs = 3 * MINUTE_IN_MILLIS;
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS),
@@ -1542,11 +1876,91 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetMaxJobExecutionTimeLocked_EJ_NewDefaultEJLimits() {
+ final long timeUsedMs = 3 * MINUTE_IN_MILLIS;
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS),
+ timeUsedMs, 5), true);
+ JobStatus job = createExpeditedJobStatus("testGetMaxJobExecutionTimeLocked_EJ", 0);
+ setStandbyBucket(RARE_INDEX, job);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job, null);
+ }
+
+ setCharging();
+ synchronized (mQuotaController.mLock) {
+ assertEquals(JobSchedulerService.Constants.DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ }
+
+ setDischarging();
+ setProcessState(getProcessStateQuotaFreeThreshold());
+ synchronized (mQuotaController.mLock) {
+ assertEquals(mQcConstants.EJ_LIMIT_WORKING_MS / 2,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ }
+
+ // Top-started job
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.prepareForExecutionLocked(job);
+ }
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ mQuotaController.maybeStopTrackingJobLocked(job, null);
+ }
+
+ setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(mQcConstants.EJ_LIMIT_RARE_MS - timeUsedMs,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ }
+
+ // Test used quota rolling out of window.
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.clearAppStatsLocked(SOURCE_USER_ID, SOURCE_PACKAGE);
+ }
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(sElapsedRealtimeClock.millis() - mQcConstants.EJ_WINDOW_SIZE_MS,
+ timeUsedMs, 5), true);
+
+ setProcessState(getProcessStateQuotaFreeThreshold());
+ synchronized (mQuotaController.mLock) {
+ // max of 50% WORKING limit and remaining quota
+ assertEquals(10 * MINUTE_IN_MILLIS,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ }
+
+ // Top-started job
+ setProcessState(ActivityManager.PROCESS_STATE_TOP);
+ synchronized (mQuotaController.mLock) {
+ mQuotaController.maybeStartTrackingJobLocked(job, null);
+ mQuotaController.prepareForExecutionLocked(job);
+ }
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(mQcConstants.EJ_LIMIT_ACTIVE_MS / 2,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ mQuotaController.maybeStopTrackingJobLocked(job, null);
+ }
+
+ setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(mQcConstants.EJ_LIMIT_RARE_MS,
+ mQuotaController.getMaxJobExecutionTimeMsLocked(job));
+ }
+ }
+
/**
* Test getTimeUntilQuotaConsumedLocked when allowed time equals the bucket window size.
*/
@Test
- public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow() {
+ @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow_LegacyDefaultBucketWindowSizes() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (8 * HOUR_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5), false);
@@ -1570,27 +1984,56 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetTimeUntilQuotaConsumedLocked_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (8 * HOUR_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5),
+ false);
+
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+ 20 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
+ // window size = allowed time, so jobs can essentially run non-stop until they reach the
+ // max execution time.
+ setStandbyBucket(EXEMPTED_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(10 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
/**
* Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
* window.
*/
@Test
- public void testGetTimeUntilQuotaConsumedLocked_BucketWindow() {
+ @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_LegacyDefaultBucketWindowSizes() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
// Close to RARE boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS),
+ createTimingSession(now - (mQcConstants.WINDOW_SIZE_RARE_MS - 30 * SECOND_IN_MILLIS),
30 * SECOND_IN_MILLIS, 5), false);
// Far away from FREQUENT boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+ createTimingSession(now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS),
+ 3 * MINUTE_IN_MILLIS, 5), false);
// Overlap WORKING_SET boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
+ createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS + MINUTE_IN_MILLIS),
3 * MINUTE_IN_MILLIS, 5), false);
// Close to ACTIVE boundary.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+ createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS),
+ 3 * MINUTE_IN_MILLIS, 5), false);
setStandbyBucket(RARE_INDEX);
synchronized (mQuotaController.mLock) {
@@ -1635,6 +2078,69 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetTimeUntilQuotaConsumedLocked_BucketWindow_NewDefaultBucketWindowSizes() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ // Close to RARE boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (mQcConstants.WINDOW_SIZE_RARE_MS - 30 * SECOND_IN_MILLIS),
+ 30 * SECOND_IN_MILLIS, 5), false);
+ // Far away from FREQUENT boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS),
+ 3 * MINUTE_IN_MILLIS, 5), false);
+ // Overlap WORKING_SET boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS + MINUTE_IN_MILLIS),
+ 3 * MINUTE_IN_MILLIS, 5), false);
+ // Close to ACTIVE boundary.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (mQcConstants.WINDOW_SIZE_ACTIVE_MS - MINUTE_IN_MILLIS),
+ 3 * MINUTE_IN_MILLIS, 5), false);
+
+ setStandbyBucket(RARE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(30 * SECOND_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ setStandbyBucket(FREQUENT_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ setStandbyBucket(WORKING_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(5 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(7 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+
+ // ACTIVE window != allowed time.
+ setStandbyBucket(ACTIVE_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(7 * MINUTE_IN_MILLIS,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(10 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
/**
* Test getTimeUntilQuotaConsumedLocked when the app is close to the max execution limit.
*/
@@ -1759,7 +2265,8 @@ public class QuotaControllerTest {
* Test getTimeUntilQuotaConsumedLocked when allowed time equals the bucket window size.
*/
@Test
- public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow() {
+ @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow_LegacyDefaultBucketWindowSizes() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - (24 * HOUR_IN_MILLIS),
@@ -1785,55 +2292,84 @@ public class QuotaControllerTest {
}
}
+ @Test
+ @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_AllowedEqualsWindow_NewDefaultBucketWindowSizes() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (24 * HOUR_IN_MILLIS),
+ mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS, 5),
+ false);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - (20 * MINUTE_IN_MILLIS), 20 * MINUTE_IN_MILLIS, 5),
+ false);
+
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+ 20 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 20 * MINUTE_IN_MILLIS);
+ // window size != allowed time.
+ setStandbyBucket(EXEMPTED_INDEX);
+ synchronized (mQuotaController.mLock) {
+ assertEquals(0,
+ mQuotaController.getRemainingExecutionTimeLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 20 * MINUTE_IN_MILLIS,
+ mQuotaController.getTimeUntilQuotaConsumedLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE));
+ }
+ }
+
/**
* Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
* window and the session is rolling out of the window.
*/
@Test
- public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow() {
+ @DisableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow_LegacyDefaultBucketWindowSizes() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (24 * HOUR_IN_MILLIS),
- 10 * MINUTE_IN_MILLIS, 5), false);
+ createTimingSession(now - mQcConstants.WINDOW_SIZE_RARE_MS,
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS, 5), false);
setStandbyBucket(RARE_INDEX);
synchronized (mQuotaController.mLock) {
assertEquals(0,
mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(10 * MINUTE_IN_MILLIS,
+ assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (8 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false);
+ createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS,
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, 5), false);
setStandbyBucket(FREQUENT_INDEX);
synchronized (mQuotaController.mLock) {
assertEquals(0,
mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(10 * MINUTE_IN_MILLIS,
+ assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (2 * HOUR_IN_MILLIS),
- 10 * MINUTE_IN_MILLIS, 5), false);
+ createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS,
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS, 5), false);
setStandbyBucket(WORKING_INDEX);
synchronized (mQuotaController.mLock) {
assertEquals(0,
mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(10 * MINUTE_IN_MILLIS,
+ assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (10 * MINUTE_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5),
- false);
+ createTimingSession(now - mQcConstants.WINDOW_SIZE_ACTIVE_MS,
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 5), false);
// ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
// max execution time.
setStandbyBucket(ACTIVE_INDEX);
@@ -1841,130 +2377,85 @@ public class QuotaControllerTest {
assertEquals(0,
mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
+ assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS
+ - (mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS
+ + mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS
+ + mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS),
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
}
}
- /**
- * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket
- * window and there are valid QuotaBumps in the history.
- */
@Test
- public void testGetTimeUntilQuotaConsumedLocked_QuotaBump() {
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS);
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
-
+ @EnableFlags(Flags.FLAG_ADJUST_QUOTA_DEFAULT_CONSTANTS)
+ public void testGetTimeUntilQuotaConsumedLocked_EdgeOfWindow_BucketWindow_NewDefaultBucketWindowSizes() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- // Close to RARE boundary.
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS),
- 30 * SECOND_IN_MILLIS, 5), false);
- mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
- .add(new QuotaBump(now - 16 * HOUR_IN_MILLIS));
- mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
- .add(new QuotaBump(now - 12 * HOUR_IN_MILLIS));
- mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
- .add(new QuotaBump(now - 8 * HOUR_IN_MILLIS));
- // Far away from FREQUENT boundary.
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
- // Overlap WORKING_SET boundary.
- mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
- .add(new QuotaBump(now - 2 * HOUR_IN_MILLIS));
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
- 3 * MINUTE_IN_MILLIS, 5), false);
- mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
- .add(new QuotaBump(now - 15 * MINUTE_IN_MILLIS));
- // Close to ACTIVE boundary.
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - mQcConstants.WINDOW_SIZE_RARE_MS,
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS, 5), false);
setStandbyBucket(RARE_INDEX);
synchronized (mQuotaController.mLock) {
- assertEquals(3 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
+ assertEquals(0,
mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(4 * MINUTE_IN_MILLIS,
+ assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_RARE_MS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
}
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS,
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, 5), false);
setStandbyBucket(FREQUENT_INDEX);
synchronized (mQuotaController.mLock) {
- assertEquals(4 * MINUTE_IN_MILLIS,
+ assertEquals(0,
mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(4 * MINUTE_IN_MILLIS,
+ assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
}
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS,
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS, 5), false);
setStandbyBucket(WORKING_INDEX);
synchronized (mQuotaController.mLock) {
- assertEquals(8 * MINUTE_IN_MILLIS,
+ assertEquals(0,
mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(10 * MINUTE_IN_MILLIS,
+ assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
}
- // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
- // max execution time.
+ mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
+ createTimingSession(now - mQcConstants.WINDOW_SIZE_ACTIVE_MS,
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 5),
+ false);
+ // ACTIVE window != allowed time.
setStandbyBucket(ACTIVE_INDEX);
synchronized (mQuotaController.mLock) {
- assertEquals(10 * MINUTE_IN_MILLIS,
+ assertEquals(0,
mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS,
+ assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
}
- }
- /**
- * Test getTimeUntilQuotaConsumedLocked when there are valid QuotaBumps in recent history that
- * provide enough additional quota to bridge gaps between sessions.
- */
- @Test
- public void testGetTimeUntilQuotaConsumedLocked_QuotaBump_CrucialBumps() {
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS);
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
-
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (25 * HOUR_IN_MILLIS),
- 30 * MINUTE_IN_MILLIS, 25), false);
- mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
- .add(new QuotaBump(now - 16 * HOUR_IN_MILLIS));
- mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
- .add(new QuotaBump(now - 12 * HOUR_IN_MILLIS));
- mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
- .add(new QuotaBump(now - 8 * HOUR_IN_MILLIS));
- // Without the valid quota bumps, the app would only 3 minutes until the quota was consumed.
- // The quota bumps provide enough quota to bridge the gap between the two earliest sessions.
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (8 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (8 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS),
- 2 * MINUTE_IN_MILLIS, 5), false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
- 3 * MINUTE_IN_MILLIS, 1), false);
- mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE)
- .add(new QuotaBump(now - 15 * MINUTE_IN_MILLIS));
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (9 * MINUTE_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 1), false);
-
- setStandbyBucket(FREQUENT_INDEX);
+ createTimingSession(now - mQcConstants.WINDOW_SIZE_EXEMPTED_MS,
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, 5),
+ false);
+ // EXEMPTED window != allowed time
+ setStandbyBucket(EXEMPTED_INDEX);
synchronized (mQuotaController.mLock) {
- assertEquals(2 * MINUTE_IN_MILLIS,
+ assertEquals(0,
mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(7 * MINUTE_IN_MILLIS,
+ assertEquals(mQcConstants.ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
}
@@ -2316,272 +2807,6 @@ public class QuotaControllerTest {
}
}
- @Test
- public void testIsWithinQuotaLocked_WithQuotaBump_Duration() {
- setDischarging();
- int standbyBucket = WORKING_INDEX;
- setStandbyBucket(standbyBucket);
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
- 5 * MINUTE_IN_MILLIS);
- setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10);
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 0);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 0);
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5);
-
- long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(
- now - (HOUR_IN_MILLIS - 2 * MINUTE_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 1),
- false);
- final ExecutionStats stats;
- synchronized (mQuotaController.mLock) {
- stats = mQuotaController.getExecutionStatsLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
- mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, 1);
- assertFalse(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(5 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
- }
- mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
- }
-
- advanceElapsedClock(HOUR_IN_MILLIS);
-
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
- }
-
- // Emulate a quota bump while some jobs are executing
- JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_Duration", 1);
- JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_Duration", 2);
-
- synchronized (mQuotaController.mLock) {
- mQuotaController.maybeStartTrackingJobLocked(job1, null);
- mQuotaController.prepareForExecutionLocked(job1);
- }
-
- advanceElapsedClock(MINUTE_IN_MILLIS);
- mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
- mQuotaController.maybeStartTrackingJobLocked(job2, null);
- mQuotaController.prepareForExecutionLocked(job2);
- }
-
- advanceElapsedClock(MINUTE_IN_MILLIS);
- synchronized (mQuotaController.mLock) {
- mQuotaController.maybeStopTrackingJobLocked(job1, null);
- mQuotaController.maybeStopTrackingJobLocked(job2, null);
- assertFalse(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
- }
-
- // Phase out the first session
- advanceElapsedClock(5 * MINUTE_IN_MILLIS);
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
- }
-
- // Phase out the first quota bump
- advanceElapsedClock(7 * HOUR_IN_MILLIS);
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs);
- }
- }
-
- @Test
- public void testIsWithinQuotaLocked_WithQuotaBump_JobCount() {
- setDischarging();
- int standbyBucket = WORKING_INDEX;
- setStandbyBucket(standbyBucket);
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
- 20 * MINUTE_IN_MILLIS);
- setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10);
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, 0);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 1);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 0);
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5);
-
- long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS - HOUR_IN_MILLIS),
- 5 * MINUTE_IN_MILLIS, 10), false);
- final ExecutionStats stats;
- synchronized (mQuotaController.mLock) {
- stats = mQuotaController.getExecutionStatsLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
- mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, 10);
- assertFalse(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(10, stats.jobCountLimit);
- }
- mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(11, stats.jobCountLimit);
- }
-
- advanceElapsedClock(HOUR_IN_MILLIS);
-
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(11, stats.jobCountLimit);
- }
-
- // Emulate a quota bump while some jobs are executing
- JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 1);
- JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 2);
-
- synchronized (mQuotaController.mLock) {
- mQuotaController.maybeStartTrackingJobLocked(job1, null);
- mQuotaController.prepareForExecutionLocked(job1);
- }
-
- advanceElapsedClock(MINUTE_IN_MILLIS);
- mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(12, stats.jobCountLimit);
- mQuotaController.maybeStartTrackingJobLocked(job2, null);
- mQuotaController.prepareForExecutionLocked(job2);
- }
-
- advanceElapsedClock(MINUTE_IN_MILLIS);
- synchronized (mQuotaController.mLock) {
- mQuotaController.maybeStopTrackingJobLocked(job1, null);
- mQuotaController.maybeStopTrackingJobLocked(job2, null);
- assertFalse(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(12, stats.jobCountLimit);
- }
-
- // Phase out the first session
- advanceElapsedClock(3 * MINUTE_IN_MILLIS);
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(12, stats.jobCountLimit);
- }
-
- // Phase out the first quota bump
- advanceElapsedClock(7 * HOUR_IN_MILLIS);
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(11, stats.jobCountLimit);
- }
- }
-
- @Test
- public void testIsWithinQuotaLocked_WithQuotaBump_SessionCount() {
- setDischarging();
- int standbyBucket = WORKING_INDEX;
- setStandbyBucket(standbyBucket);
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
- 20 * MINUTE_IN_MILLIS);
- setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 2);
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, 0);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 0);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 1);
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5);
-
- long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (mQcConstants.WINDOW_SIZE_WORKING_MS - HOUR_IN_MILLIS),
- 5 * MINUTE_IN_MILLIS, 1), false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (30 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), false);
- final ExecutionStats stats;
- synchronized (mQuotaController.mLock) {
- stats = mQuotaController.getExecutionStatsLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
- assertFalse(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(2, stats.sessionCountLimit);
- }
- mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(3, stats.sessionCountLimit);
- }
-
- advanceElapsedClock(HOUR_IN_MILLIS);
-
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(3, stats.sessionCountLimit);
- }
-
- // Emulate a quota bump while some jobs are executing
- JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 1);
- JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 2);
-
- synchronized (mQuotaController.mLock) {
- mQuotaController.maybeStartTrackingJobLocked(job1, null);
- mQuotaController.prepareForExecutionLocked(job1);
- }
-
- advanceElapsedClock(MINUTE_IN_MILLIS);
- mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID);
- synchronized (mQuotaController.mLock) {
- mQuotaController.maybeStopTrackingJobLocked(job1, null);
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(4, stats.sessionCountLimit);
- }
-
- advanceElapsedClock(MINUTE_IN_MILLIS);
- synchronized (mQuotaController.mLock) {
- mQuotaController.maybeStartTrackingJobLocked(job2, null);
- mQuotaController.prepareForExecutionLocked(job2);
- }
-
- advanceElapsedClock(MINUTE_IN_MILLIS);
- synchronized (mQuotaController.mLock) {
- mQuotaController.maybeStopTrackingJobLocked(job2, null);
- assertFalse(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(4, stats.sessionCountLimit);
- }
-
- // Phase out the first session
- advanceElapsedClock(2 * MINUTE_IN_MILLIS);
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(4, stats.sessionCountLimit);
- }
-
- // Phase out the first quota bump
- advanceElapsedClock(7 * HOUR_IN_MILLIS);
- synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController
- .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX));
- assertEquals(3, stats.sessionCountLimit);
- }
- }
@Test
public void testIsWithinEJQuotaLocked_NeverApp() {
@@ -3590,12 +3815,6 @@ public class QuotaControllerTest {
setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS,
84 * SECOND_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 83 * SECOND_IN_MILLIS);
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS,
- 93 * SECOND_IN_MILLIS);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 92);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 91);
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 90 * MINUTE_IN_MILLIS);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 89);
assertEquals(8 * MINUTE_IN_MILLIS,
mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
@@ -3653,11 +3872,6 @@ public class QuotaControllerTest {
assertEquals(85 * SECOND_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs());
assertEquals(84 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs());
assertEquals(83 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs());
- assertEquals(93 * SECOND_IN_MILLIS, mQuotaController.getQuotaBumpAdditionDurationMs());
- assertEquals(92, mQuotaController.getQuotaBumpAdditionJobCount());
- assertEquals(91, mQuotaController.getQuotaBumpAdditionSessionCount());
- assertEquals(90 * MINUTE_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs());
- assertEquals(89, mQuotaController.getQuotaBumpLimit());
}
@Test
@@ -3710,11 +3924,6 @@ public class QuotaControllerTest {
setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, -1);
setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, -1);
setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, -1);
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, -1);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, -1);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, -1);
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 59 * MINUTE_IN_MILLIS);
- setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, -1);
assertEquals(MINUTE_IN_MILLIS,
mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
@@ -3765,11 +3974,6 @@ public class QuotaControllerTest {
assertEquals(0, mQuotaController.getEJRewardNotificationSeenMs());
assertEquals(0, mQuotaController.getEJGracePeriodTempAllowlistMs());
assertEquals(0, mQuotaController.getEJGracePeriodTopAppMs());
- assertEquals(0, mQuotaController.getQuotaBumpAdditionDurationMs());
- assertEquals(0, mQuotaController.getQuotaBumpAdditionJobCount());
- assertEquals(0, mQuotaController.getQuotaBumpAdditionSessionCount());
- assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs());
- assertEquals(0, mQuotaController.getQuotaBumpLimit());
// Invalid configurations.
// In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD
@@ -3827,7 +4031,6 @@ public class QuotaControllerTest {
setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 25 * HOUR_IN_MILLIS);
- setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 25 * HOUR_IN_MILLIS);
assertEquals(24 * HOUR_IN_MILLIS,
mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
@@ -3868,7 +4071,6 @@ public class QuotaControllerTest {
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs());
assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs());
assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs());
- assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs());
}
/** Tests that TimingSessions aren't saved when the device is charging. */
@@ -4157,7 +4359,7 @@ public class QuotaControllerTest {
/** Tests that Timers count FOREGROUND_SERVICE jobs. */
@Test
- @RequiresFlagsEnabled(FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS)
+ @EnableFlags(Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS)
public void testTimerTracking_Fgs() {
setDischarging();
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
index 9ba272446689..625dbe6e16f9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import static android.media.AudioAttributes.USAGE_ALARM;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -91,6 +92,7 @@ public class BackgroundUserSoundNotifierTest {
}
@Test
public void testAlarmOnBackgroundUser_foregroundUserNotified() throws RemoteException {
+ assumeTrue(UserManager.supportsMultipleUsers());
AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build();
UserInfo user = createUser("User", UserManager.USER_TYPE_FULL_SECONDARY, 0);
final int fgUserId = mSpiedContext.getUserId();
@@ -100,7 +102,7 @@ public class BackgroundUserSoundNotifierTest {
/* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN,
AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
clearInvocations(mNotificationManager);
- mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+ mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
verify(mNotificationManager)
.notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()),
eq(afi.getClientUid()), any(Notification.class),
@@ -116,7 +118,7 @@ public class BackgroundUserSoundNotifierTest {
/* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN,
AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT);
clearInvocations(mNotificationManager);
- mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+ mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
verifyZeroInteractions(mNotificationManager);
}
@@ -131,7 +133,7 @@ public class BackgroundUserSoundNotifierTest {
AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0,
Build.VERSION.SDK_INT);
clearInvocations(mNotificationManager);
- mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext);
+ mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi);
verifyZeroInteractions(mNotificationManager);
}
@@ -169,7 +171,7 @@ public class BackgroundUserSoundNotifierTest {
doReturn(focusStack).when(mockAudioPolicy).getFocusStack();
mBackgroundUserSoundNotifier.mFocusControlAudioPolicy = mockAudioPolicy;
- mBackgroundUserSoundNotifier.muteAlarmSounds(mSpiedContext);
+ mBackgroundUserSoundNotifier.muteAlarmSounds(bgUserUid);
verify(apc1.getPlayerProxy()).stop();
verify(mockAudioPolicy).sendFocusLossAndUpdate(afi);
@@ -178,6 +180,7 @@ public class BackgroundUserSoundNotifierTest {
@Test
public void testOnAudioFocusGrant_alarmOnBackgroundUser_notifiesForegroundUser() {
+ assumeTrue(UserManager.supportsMultipleUsers());
final int fgUserId = mSpiedContext.getUserId();
UserInfo bgUser = createUser("Background User", UserManager.USER_TYPE_FULL_SECONDARY, 0);
int bgUserUid = bgUser.id * 100000;
@@ -205,7 +208,7 @@ public class BackgroundUserSoundNotifierTest {
.when(mUserManager).getUserSwitchability(any());
Notification notification = mBackgroundUserSoundNotifier.createNotification(userName,
- mSpiedContext);
+ mSpiedContext, 101000);
assertEquals("Alarm for BgUser", notification.extras.getString(
Notification.EXTRA_TITLE));
@@ -232,7 +235,7 @@ public class BackgroundUserSoundNotifierTest {
.when(mUserManager).getUserSwitchability(any());
Notification notification = mBackgroundUserSoundNotifier.createNotification(userName,
- mSpiedContext);
+ mSpiedContext, 101000);
assertEquals(1, notification.actions.length);
assertEquals(mSpiedContext.getString(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
index e131a98b52d0..de029e0d770f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
@@ -185,7 +185,8 @@ class DistractingPackageHelperTest : PackageHelperTestBase() {
verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
verify(broadcastHelper, never()).sendPackageBroadcast(eq(
Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(),
- nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable())
+ nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable(),
+ nullable())
distractingPackageHelper.removeDistractingPackageRestrictions(pms.snapshotComputer(),
arrayOfNulls(0), TEST_USER_ID)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 43a8aa957fa5..124c41ed449f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -741,7 +741,8 @@ public class StagingManagerTest {
/* stagedSessionErrorCode */ PackageManager.INSTALL_UNKNOWN,
/* stagedSessionErrorMessage */ "no error",
/* preVerifiedDomains */ null,
- /* verifierController */ null);
+ /* verifierController */ null,
+ /* verificationPolicy */ PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED);
StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
doReturn(packageName).when(stagedSession).getPackageName();
diff --git a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 639ae30c00b9..58489f398775 100644
--- a/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -81,7 +81,6 @@ import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -581,8 +580,6 @@ public class HintManagerServiceTest {
HintManagerService service = createService();
IBinder token = new Binder();
int threadCount = 2;
-
- // session 1 has 2 non-isolated tids
long sessionPtr1 = 111;
CountDownLatch stopLatch1 = new CountDownLatch(1);
int[] tids1 = createThreads(threadCount, stopLatch1);
@@ -618,7 +615,6 @@ public class HintManagerServiceTest {
IBinder token = new Binder();
int threadCount = 2;
- // session 1 has 2 non-isolated tids
long sessionPtr1 = 111;
CountDownLatch stopLatch1 = new CountDownLatch(1);
int[] tids1 = createThreads(threadCount, stopLatch1);
@@ -630,67 +626,23 @@ public class HintManagerServiceTest {
SessionTag.OTHER, new SessionConfig());
assertNotNull(session1);
- // session 2 has 2 non-isolated tids and 2 isolated tids
- long sessionPtr2 = 222;
- CountDownLatch stopLatch2 = new CountDownLatch(1);
- // negative value used for test only to avoid conflicting with any real thread that exists
- int isoProc1 = -100;
- int isoProc2 = 99999999;
- when(mAmInternalMock.getIsolatedProcesses(eq(UID))).thenReturn(List.of(0));
- int[] tids2 = createThreads(threadCount, stopLatch2);
- int[] tids2WithIsolated = Arrays.copyOf(tids2, tids2.length + 2);
- tids2WithIsolated[threadCount] = isoProc1;
- tids2WithIsolated[threadCount + 1] = isoProc2;
- int[] expectedTids2 = Arrays.copyOf(tids2, tids2.length + 1);
- expectedTids2[tids2.length] = isoProc1;
- when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID),
- eq(tids2WithIsolated), eq(DEFAULT_TARGET_DURATION), anyInt(),
- any(SessionConfig.class))).thenReturn(sessionPtr2);
- AppHintSession session2 = (AppHintSession) service.getBinderServiceInstance()
- .createHintSessionWithConfig(token, tids2WithIsolated,
- DEFAULT_TARGET_DURATION, SessionTag.OTHER, new SessionConfig());
- assertNotNull(session2);
-
- // trigger clean up through UID state change by making the process foreground->background
- // this will remove the one unexpected isolated tid from session 2
- service.mUidObserver.onUidStateChanged(UID,
- ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
- service.mUidObserver.onUidStateChanged(UID,
- ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
- LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos(
- CLEAN_UP_UID_DELAY_MILLIS));
- verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
- verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any());
- // the new TIDs pending list should be updated
- assertArrayEquals(expectedTids2, session2.getTidsInternal());
- reset(mNativeWrapperMock);
-
- // this should resume and update the threads so those never-existed invalid isolated
- // processes should be cleaned up
- service.mUidObserver.onUidStateChanged(UID,
- ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
- // wait for the async uid state change to trigger resume and setThreads
- LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000));
- verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr2), eq(expectedTids2));
- reset(mNativeWrapperMock);
-
// let all session 1 threads to exit and the cleanup should force pause the session 1
stopLatch1.countDown();
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
service.mUidObserver.onUidStateChanged(UID,
+ ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
+ service.mUidObserver.onUidStateChanged(UID,
ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000) + TimeUnit.MILLISECONDS.toNanos(
CLEAN_UP_UID_DELAY_MILLIS));
verify(mNativeWrapperMock, times(1)).halPauseHintSession(eq(sessionPtr1));
verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
- verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any());
verifyAllHintsEnabled(session1, false);
- verifyAllHintsEnabled(session2, false);
service.mUidObserver.onUidStateChanged(UID,
ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1000));
+ assertArrayEquals(tids1, session1.getTidsInternal());
verifyAllHintsEnabled(session1, false);
- verifyAllHintsEnabled(session2, true);
reset(mNativeWrapperMock);
// in foreground, set new tids for session 1 then it should be resumed and all hints allowed
@@ -704,8 +656,6 @@ public class HintManagerServiceTest {
// let all session 1 and 2 non isolated threads to exit
stopLatch1.countDown();
- stopLatch2.countDown();
- expectedTids2 = new int[]{isoProc1};
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
service.mUidObserver.onUidStateChanged(UID,
ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
@@ -713,14 +663,11 @@ public class HintManagerServiceTest {
CLEAN_UP_UID_DELAY_MILLIS));
verify(mNativeWrapperMock, times(1)).halPauseHintSession(eq(sessionPtr1));
verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
- verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any());
// in background, set threads for session 1 then it should not be force paused next time
session1.setThreads(SESSION_TIDS_A);
// the new TIDs pending list should be updated
assertArrayEquals(SESSION_TIDS_A, session1.getTidsInternal());
- assertArrayEquals(expectedTids2, session2.getTidsInternal());
verifyAllHintsEnabled(session1, false);
- verifyAllHintsEnabled(session2, false);
reset(mNativeWrapperMock);
service.mUidObserver.onUidStateChanged(UID,
@@ -729,10 +676,7 @@ public class HintManagerServiceTest {
CLEAN_UP_UID_DELAY_MILLIS));
verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr1),
eq(SESSION_TIDS_A));
- verify(mNativeWrapperMock, times(1)).halSetThreads(eq(sessionPtr2),
- eq(expectedTids2));
verifyAllHintsEnabled(session1, true);
- verifyAllHintsEnabled(session2, true);
}
private void verifyAllHintsEnabled(AppHintSession session, boolean verifyEnabled) {
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index a1db18232c09..1c7fc63efd41 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -54,6 +54,7 @@ import android.os.test.TestLooper;
import android.provider.Settings;
import android.testing.TestableContext;
import android.util.IntArray;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.DisplayAddress;
@@ -384,6 +385,32 @@ public class NotifierTest {
}
@Test
+ public void testOnGroupRemoved_perDisplayWakeByTouchEnabled() {
+ createNotifier();
+ // GIVEN per-display wake by touch is enabled and one display group has been defined
+ when(mPowerManagerFlags.isPerDisplayWakeByTouchEnabled()).thenReturn(true);
+ final int groupId = 313;
+ final int displayId1 = 3113;
+ final int displayId2 = 4114;
+ final int[] displays = new int[]{displayId1, displayId2};
+ when(mDisplayManagerInternal.getDisplayIds()).thenReturn(IntArray.wrap(displays));
+ when(mDisplayManagerInternal.getDisplayIdsForGroup(groupId)).thenReturn(displays);
+ mNotifier.onGroupWakefulnessChangeStarted(
+ groupId, WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_TAP, /* eventTime= */ 1000);
+ final SparseBooleanArray expectedDisplayInteractivities = new SparseBooleanArray();
+ expectedDisplayInteractivities.put(displayId1, true);
+ expectedDisplayInteractivities.put(displayId2, true);
+ verify(mInputManagerInternal).setDisplayInteractivities(expectedDisplayInteractivities);
+
+ // WHEN display group is removed
+ when(mDisplayManagerInternal.getDisplayIdsByGroupsIds()).thenReturn(new SparseArray<>());
+ mNotifier.onGroupRemoved(groupId);
+
+ // THEN native input manager is informed that displays in that group no longer exist
+ verify(mInputManagerInternal).setDisplayInteractivities(new SparseBooleanArray());
+ }
+
+ @Test
public void testOnWakeLockListener_RemoteException_NoRethrow() throws RemoteException {
when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
createNotifier();
diff --git a/services/tests/security/forensic/Android.bp b/services/tests/security/forensic/Android.bp
new file mode 100644
index 000000000000..adc49040a004
--- /dev/null
+++ b/services/tests/security/forensic/Android.bp
@@ -0,0 +1,41 @@
+package {
+ default_team: "trendy_team_platform_security",
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "ForensicServiceTests",
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "androidx.test.runner",
+ "compatibility-device-util-axt",
+ "frameworks-base-testutils",
+ "junit",
+ "platform-test-annotations",
+ "services.core",
+ "truth",
+ ],
+
+ platform_apis: true,
+
+ test_suites: [
+ "device-tests",
+ "automotive-tests",
+ ],
+
+ certificate: "platform",
+ dxflags: ["--multi-dex"],
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/security/forensic/AndroidManifest.xml b/services/tests/security/forensic/AndroidManifest.xml
new file mode 100644
index 000000000000..40feb19aecba
--- /dev/null
+++ b/services/tests/security/forensic/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.security.forensic.tests">
+
+ <application android:testOnly="true">
+ <uses-library android:name="android.test.runner"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.security.forensic.tests"
+ android:label="Frameworks Forensic Services Tests"/>
+</manifest>
diff --git a/services/tests/security/forensic/AndroidTest.xml b/services/tests/security/forensic/AndroidTest.xml
new file mode 100644
index 000000000000..bbe2e9c303ce
--- /dev/null
+++ b/services/tests/security/forensic/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs Frameworks Forensic Service tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="ForensicServiceTests.apk"/>
+ <option name="install-arg" value="-t" />
+ </target_preparer>
+
+ <option name="test-tag" value="ForensicServiceTests" />
+ <test class="com.android.tradefed.testtype.InstrumentationTest" >
+ <option name="package" value="com.android.server.security.forensic.tests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/security/forensic/TEST_MAPPING b/services/tests/security/forensic/TEST_MAPPING
new file mode 100644
index 000000000000..bd8b2ab7c41f
--- /dev/null
+++ b/services/tests/security/forensic/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "ForensicServiceTests"
+ }
+ ]
+}
diff --git a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
new file mode 100644
index 000000000000..7aa2e0f609a7
--- /dev/null
+++ b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
@@ -0,0 +1,387 @@
+/*
+ * 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.security.forensic;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.security.forensic.IForensicServiceCommandCallback;
+import android.security.forensic.IForensicServiceStateCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class ForensicServiceTest {
+ private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
+ private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE;
+ private static final int STATE_VISIBLE = IForensicServiceStateCallback.State.VISIBLE;
+ private static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED;
+
+ private static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN;
+ private static final int ERROR_PERMISSION_DENIED =
+ IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED;
+ private static final int ERROR_INVALID_STATE_TRANSITION =
+ IForensicServiceCommandCallback.ErrorCode.INVALID_STATE_TRANSITION;
+ private static final int ERROR_BACKUP_TRANSPORT_UNAVAILABLE =
+ IForensicServiceCommandCallback.ErrorCode.BACKUP_TRANSPORT_UNAVAILABLE;
+ private static final int ERROR_DATA_SOURCE_UNAVAILABLE =
+ IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE;
+
+ @Mock
+ private Context mContextSpy;
+
+ private ForensicService mForensicService;
+ private TestLooper mTestLooper;
+ private Looper mLooper;
+
+ @SuppressLint("VisibleForTests")
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mTestLooper = new TestLooper();
+ mLooper = mTestLooper.getLooper();
+ mForensicService = new ForensicService(new MockInjector(mContextSpy));
+ mForensicService.onStart();
+ }
+
+ @Test
+ public void testMonitorState_Invisible() throws RemoteException {
+ StateCallback scb = new StateCallback();
+ assertEquals(STATE_UNKNOWN, scb.mState);
+ mForensicService.getBinderService().monitorState(scb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb.mState);
+ }
+
+ @Test
+ public void testMonitorState_Invisible_TwoMonitors() throws RemoteException {
+ StateCallback scb1 = new StateCallback();
+ assertEquals(STATE_UNKNOWN, scb1.mState);
+ mForensicService.getBinderService().monitorState(scb1);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+
+ StateCallback scb2 = new StateCallback();
+ assertEquals(STATE_UNKNOWN, scb2.mState);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+ }
+
+ @Test
+ public void testMakeVisible_FromInvisible() throws RemoteException {
+ StateCallback scb = new StateCallback();
+ assertEquals(STATE_UNKNOWN, scb.mState);
+ mForensicService.getBinderService().monitorState(scb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb.mState);
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().makeVisible(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_VISIBLE, scb.mState);
+ assertNull(ccb.mErrorCode);
+ }
+
+ @Test
+ public void testMakeVisible_FromInvisible_TwoMonitors() throws RemoteException {
+ mForensicService.setState(STATE_INVISIBLE);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().makeVisible(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_VISIBLE, scb1.mState);
+ assertEquals(STATE_VISIBLE, scb2.mState);
+ assertNull(ccb.mErrorCode);
+ }
+
+ @Test
+ public void testMakeVisible_FromVisible_TwoMonitors() throws RemoteException {
+ mForensicService.setState(STATE_VISIBLE);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_VISIBLE, scb1.mState);
+ assertEquals(STATE_VISIBLE, scb2.mState);
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().makeVisible(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_VISIBLE, scb1.mState);
+ assertEquals(STATE_VISIBLE, scb2.mState);
+ assertNull(ccb.mErrorCode);
+ }
+
+ @Test
+ public void testMakeVisible_FromEnabled_TwoMonitors() throws RemoteException {
+ mForensicService.setState(STATE_ENABLED);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_ENABLED, scb1.mState);
+ assertEquals(STATE_ENABLED, scb2.mState);
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().makeVisible(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_ENABLED, scb1.mState);
+ assertEquals(STATE_ENABLED, scb2.mState);
+ assertNotNull(ccb.mErrorCode);
+ assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue());
+ }
+
+ @Test
+ public void testMakeInvisible_FromInvisible_TwoMonitors() throws RemoteException {
+ mForensicService.setState(STATE_INVISIBLE);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().makeInvisible(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertNull(ccb.mErrorCode);
+ }
+
+ @Test
+ public void testMakeInvisible_FromVisible_TwoMonitors() throws RemoteException {
+ mForensicService.setState(STATE_VISIBLE);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_VISIBLE, scb1.mState);
+ assertEquals(STATE_VISIBLE, scb2.mState);
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().makeInvisible(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertNull(ccb.mErrorCode);
+ }
+
+ @Test
+ public void testMakeInvisible_FromEnabled_TwoMonitors() throws RemoteException {
+ mForensicService.setState(STATE_ENABLED);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_ENABLED, scb1.mState);
+ assertEquals(STATE_ENABLED, scb2.mState);
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().makeInvisible(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertNull(ccb.mErrorCode);
+ }
+
+
+ @Test
+ public void testEnable_FromInvisible_TwoMonitors() throws RemoteException {
+ mForensicService.setState(STATE_INVISIBLE);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().enable(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertNotNull(ccb.mErrorCode);
+ assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue());
+ }
+
+ @Test
+ public void testEnable_FromVisible_TwoMonitors() throws RemoteException {
+ mForensicService.setState(STATE_VISIBLE);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_VISIBLE, scb1.mState);
+ assertEquals(STATE_VISIBLE, scb2.mState);
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().enable(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_ENABLED, scb1.mState);
+ assertEquals(STATE_ENABLED, scb2.mState);
+ assertNull(ccb.mErrorCode);
+ }
+
+ @Test
+ public void testEnable_FromEnabled_TwoMonitors() throws RemoteException {
+ mForensicService.setState(STATE_ENABLED);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_ENABLED, scb1.mState);
+ assertEquals(STATE_ENABLED, scb2.mState);
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().enable(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_ENABLED, scb1.mState);
+ assertEquals(STATE_ENABLED, scb2.mState);
+ assertNull(ccb.mErrorCode);
+ }
+
+ @Test
+ public void testDisable_FromInvisible_TwoMonitors() throws RemoteException {
+ mForensicService.setState(STATE_INVISIBLE);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().disable(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertNotNull(ccb.mErrorCode);
+ assertEquals(ERROR_INVALID_STATE_TRANSITION, ccb.mErrorCode.intValue());
+ }
+
+ @Test
+ public void testDisable_FromVisible_TwoMonitors() throws RemoteException {
+ mForensicService.setState(STATE_VISIBLE);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_VISIBLE, scb1.mState);
+ assertEquals(STATE_VISIBLE, scb2.mState);
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().disable(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_VISIBLE, scb1.mState);
+ assertEquals(STATE_VISIBLE, scb2.mState);
+ assertNull(ccb.mErrorCode);
+ }
+
+ @Test
+ public void testDisable_FromEnabled_TwoMonitors() throws RemoteException {
+ mForensicService.setState(STATE_ENABLED);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_ENABLED, scb1.mState);
+ assertEquals(STATE_ENABLED, scb2.mState);
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().disable(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_VISIBLE, scb1.mState);
+ assertEquals(STATE_VISIBLE, scb2.mState);
+ assertNull(ccb.mErrorCode);
+ }
+
+ private class MockInjector implements ForensicService.Injector {
+ private final Context mContext;
+
+ MockInjector(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+
+ @Override
+ public Looper getLooper() {
+ return mLooper;
+ }
+
+ }
+
+ private static class StateCallback extends IForensicServiceStateCallback.Stub {
+ int mState = STATE_UNKNOWN;
+
+ @Override
+ public void onStateChange(int state) throws RemoteException {
+ mState = state;
+ }
+ }
+
+ private static class CommandCallback extends IForensicServiceCommandCallback.Stub {
+ Integer mErrorCode = null;
+
+ public void reset() {
+ mErrorCode = null;
+ }
+
+ @Override
+ public void onSuccess() throws RemoteException {
+
+ }
+
+ @Override
+ public void onFailure(int errorCode) throws RemoteException {
+ mErrorCode = errorCode;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index b745e6a7d4a5..e5831b326de5 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -1417,7 +1417,7 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseMoveEventsDoNotMoveMagnifierViewport() {
runMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE);
}
@@ -1471,55 +1471,55 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseHoverMoveEventsDoNotMoveMagnifierViewport() {
runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE);
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testStylusHoverMoveEventsDoNotMoveMagnifierViewport() {
runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_STYLUS);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseHoverMoveEventsMoveMagnifierViewport() {
runHoverMovesViewportTest(InputDevice.SOURCE_MOUSE);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testStylusHoverMoveEventsMoveMagnifierViewport() {
runHoverMovesViewportTest(InputDevice.SOURCE_STYLUS);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseDownEventsDoNotMoveMagnifierViewport() {
runDownDoesNotMoveViewportTest(InputDevice.SOURCE_MOUSE);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testStylusDownEventsDoNotMoveMagnifierViewport() {
runDownDoesNotMoveViewportTest(InputDevice.SOURCE_STYLUS);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseUpEventsDoNotMoveMagnifierViewport() {
runUpDoesNotMoveViewportTest(InputDevice.SOURCE_MOUSE);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testStylusUpEventsDoNotMoveMagnifierViewport() {
runUpDoesNotMoveViewportTest(InputDevice.SOURCE_STYLUS);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseMoveEventsMoveMagnifierViewport() {
final EventCaptor eventCaptor = new EventCaptor();
mMgh.setNext(eventCaptor);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
index d80a1f056e94..45c157d1c1a0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
@@ -93,7 +93,7 @@ public class MagnificationGestureHandlerTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void onMotionEvent_isFromMouse_handleMouseOrStylusEvent() {
final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
mouseEvent.setSource(InputDevice.SOURCE_MOUSE);
@@ -108,7 +108,7 @@ public class MagnificationGestureHandlerTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void onMotionEvent_isFromStylus_handleMouseOrStylusEvent() {
final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
stylusEvent.setSource(InputDevice.SOURCE_STYLUS);
@@ -123,7 +123,7 @@ public class MagnificationGestureHandlerTest {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void onMotionEvent_isFromMouse_handleMouseOrStylusEventNotCalled() {
final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
mouseEvent.setSource(InputDevice.SOURCE_MOUSE);
@@ -138,7 +138,7 @@ public class MagnificationGestureHandlerTest {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+ @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void onMotionEvent_isFromStylus_handleMouseOrStylusEventNotCalled() {
final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
stylusEvent.setSource(InputDevice.SOURCE_STYLUS);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index d1f6c2f9f1f0..9c6412b81b34 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -67,10 +67,8 @@ import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import com.android.internal.R;
-import com.android.internal.pm.parsing.PackageParser2;
import com.android.server.compat.PlatformCompat;
import com.android.server.integrity.model.IntegrityCheckResult;
-import com.android.server.pm.parsing.TestPackageParser2;
import com.android.server.testutils.TestUtils;
import org.junit.After;
@@ -140,8 +138,6 @@ public class AppIntegrityManagerServiceImplTest {
@Mock IntegrityFileManager mIntegrityFileManager;
@Mock Handler mHandler;
- private Supplier<PackageParser2> mParserSupplier = TestPackageParser2::new;
-
private final Context mRealContext = InstrumentationRegistry.getTargetContext();
private PackageManager mSpyPackageManager;
@@ -173,7 +169,6 @@ public class AppIntegrityManagerServiceImplTest {
new AppIntegrityManagerServiceImpl(
mMockContext,
mPackageManagerInternal,
- mParserSupplier,
mIntegrityFileManager,
mHandler);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
index a54501029712..f45eddcf4480 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.when;
import android.database.Cursor;
import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.provider.CallLog.Calls;
import android.test.mock.MockContentProvider;
@@ -58,6 +59,7 @@ public final class CallLogQueryHelperTest {
private MatrixCursor mCursor;
private EventConsumer mEventConsumer;
private CallLogQueryHelper mHelper;
+ private CallLogContentProvider mCallLogContentProvider;
@Before
public void setUp() {
@@ -66,7 +68,8 @@ public final class CallLogQueryHelperTest {
mCursor = new MatrixCursor(CALL_LOG_COLUMNS);
MockContentResolver contentResolver = new MockContentResolver();
- contentResolver.addProvider(CALL_LOG_AUTHORITY, new CallLogContentProvider());
+ mCallLogContentProvider = new CallLogContentProvider();
+ contentResolver.addProvider(CALL_LOG_AUTHORITY, mCallLogContentProvider);
when(mContext.getContentResolver()).thenReturn(contentResolver);
mEventConsumer = new EventConsumer();
@@ -80,6 +83,12 @@ public final class CallLogQueryHelperTest {
}
@Test
+ public void testQueryWithSQLiteException() {
+ mCallLogContentProvider.setThrowSQLiteException(true);
+ assertFalse(mHelper.querySince(50L));
+ }
+
+ @Test
public void testQueryIncomingCall() {
mCursor.addRow(new Object[] {
NORMALIZED_PHONE_NUMBER, /* date= */ 100L, /* duration= */ 30L,
@@ -159,11 +168,20 @@ public final class CallLogQueryHelperTest {
}
private class CallLogContentProvider extends MockContentProvider {
+ private boolean mThrowSQLiteException = false;
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
+ if (mThrowSQLiteException) {
+ throw new SQLiteException();
+ }
+
return mCursor;
}
+
+ public void setThrowSQLiteException(boolean throwException) {
+ this.mThrowSQLiteException = throwException;
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
index 16a02b678511..1daee39ce9de 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ContactsQueryHelperTest.java
@@ -99,6 +99,14 @@ public final class ContactsQueryHelperTest {
}
@Test
+ public void testQueryOtherException_returnsFalse() {
+ contentProvider.setThrowOtherException(true);
+
+ Uri contactUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, CONTACT_LOOKUP_KEY);
+ assertFalse(mHelper.query(contactUri.toString()));
+ }
+
+ @Test
public void testQueryIllegalArgumentException_returnsFalse() {
contentProvider.setThrowIllegalArgumentException(true);
@@ -152,6 +160,13 @@ public final class ContactsQueryHelperTest {
}
@Test
+ public void testQueryWithPhoneNumber_otherExceptionReturnsFalse() {
+ contentProvider.setThrowOtherException(true);
+ String contactUri = "tel:" + PHONE_NUMBER;
+ assertFalse(mHelper.query(contactUri));
+ }
+
+ @Test
public void testQueryWithEmail() {
mContactsLookupCursor.addRow(new Object[] {
/* id= */ 11, CONTACT_LOOKUP_KEY, /* starred= */ 1, /* hasPhoneNumber= */ 0 });
@@ -188,6 +203,7 @@ public final class ContactsQueryHelperTest {
private Map<Uri, Cursor> mUriPrefixToCursorMap = new ArrayMap<>();
private boolean mThrowSQLiteException = false;
private boolean mThrowIllegalArgumentException = false;
+ private boolean mThrowOtherException = false;
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
@@ -198,6 +214,9 @@ public final class ContactsQueryHelperTest {
if (mThrowIllegalArgumentException) {
throw new IllegalArgumentException();
}
+ if (mThrowOtherException) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
for (Uri prefixUri : mUriPrefixToCursorMap.keySet()) {
if (uri.isPathPrefixMatch(prefixUri)) {
@@ -215,6 +234,10 @@ public final class ContactsQueryHelperTest {
this.mThrowIllegalArgumentException = throwException;
}
+ public void setThrowOtherException(boolean throwException) {
+ this.mThrowOtherException = throwException;
+ }
+
private void registerCursor(Uri uriPrefix, Cursor cursor) {
mUriPrefixToCursorMap.put(uriPrefix, cursor);
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java
index 7730890e1486..9f4a43df6de8 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/MmsQueryHelperTest.java
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.when;
import android.database.Cursor;
import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.provider.Telephony.BaseMmsColumns;
import android.provider.Telephony.Mms;
@@ -63,6 +64,7 @@ public final class MmsQueryHelperTest {
private final List<MatrixCursor> mAddrCursors = new ArrayList<>();
private EventConsumer mEventConsumer;
private MmsQueryHelper mHelper;
+ private MmsContentProvider mMmsContentProvider;
@Before
public void setUp() {
@@ -73,7 +75,8 @@ public final class MmsQueryHelperTest {
mAddrCursors.add(new MatrixCursor(ADDR_COLUMNS));
MockContentResolver contentResolver = new MockContentResolver();
- contentResolver.addProvider(MMS_AUTHORITY, new MmsContentProvider());
+ mMmsContentProvider = new MmsContentProvider();
+ contentResolver.addProvider(MMS_AUTHORITY, mMmsContentProvider);
when(mContext.getContentResolver()).thenReturn(contentResolver);
mEventConsumer = new EventConsumer();
@@ -87,6 +90,12 @@ public final class MmsQueryHelperTest {
}
@Test
+ public void testQueryWithSQLiteException() {
+ mMmsContentProvider.setThrowSQLiteException(true);
+ assertFalse(mHelper.querySince(50_000L));
+ }
+
+ @Test
public void testQueryIncomingMessage() {
mMmsCursor.addRow(new Object[] {
/* id= */ 0, /* date= */ 100L, /* msgBox= */ BaseMmsColumns.MESSAGE_BOX_INBOX });
@@ -159,10 +168,15 @@ public final class MmsQueryHelperTest {
}
private class MmsContentProvider extends MockContentProvider {
+ private boolean mThrowSQLiteException = false;
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
+ if (mThrowSQLiteException) {
+ throw new SQLiteException();
+ }
+
List<String> segments = uri.getPathSegments();
if (segments.size() == 2 && "addr".equals(segments.get(1))) {
int messageId = Integer.valueOf(segments.get(0));
@@ -170,5 +184,9 @@ public final class MmsQueryHelperTest {
}
return mMmsCursor;
}
+
+ public void setThrowSQLiteException(boolean throwException) {
+ this.mThrowSQLiteException = throwException;
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java
index 5cb8cb4fe9f1..09a0dff77eb0 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/SmsQueryHelperTest.java
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.when;
import android.database.Cursor;
import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.provider.Telephony.Sms;
import android.provider.Telephony.TextBasedSmsColumns;
@@ -59,6 +60,7 @@ public final class SmsQueryHelperTest {
private MatrixCursor mSmsCursor;
private EventConsumer mEventConsumer;
private SmsQueryHelper mHelper;
+ private SmsContentProvider mSmsContentProvider;
@Before
public void setUp() {
@@ -67,7 +69,8 @@ public final class SmsQueryHelperTest {
mSmsCursor = new MatrixCursor(SMS_COLUMNS);
MockContentResolver contentResolver = new MockContentResolver();
- contentResolver.addProvider(SMS_AUTHORITY, new SmsContentProvider());
+ mSmsContentProvider = new SmsContentProvider();
+ contentResolver.addProvider(SMS_AUTHORITY, mSmsContentProvider);
when(mContext.getContentResolver()).thenReturn(contentResolver);
mEventConsumer = new EventConsumer();
@@ -130,6 +133,12 @@ public final class SmsQueryHelperTest {
assertEquals(110L, events.get(1).getTimestamp());
}
+ @Test
+ public void testQueryWithSQLiteException() {
+ mSmsContentProvider.setThrowSQLiteException(true);
+ assertFalse(mHelper.querySince(50L));
+ }
+
private class EventConsumer implements BiConsumer<String, Event> {
private final Map<String, List<Event>> mEventMap = new ArrayMap<>();
@@ -141,11 +150,19 @@ public final class SmsQueryHelperTest {
}
private class SmsContentProvider extends MockContentProvider {
+ private boolean mThrowSQLiteException = false;
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
+ if (mThrowSQLiteException) {
+ throw new SQLiteException();
+ }
return mSmsCursor;
}
+
+ public void setThrowSQLiteException(boolean throwException) {
+ this.mThrowSQLiteException = throwException;
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigReadOnlyFeaturesTest.kt b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigReadOnlyFeaturesTest.kt
new file mode 100644
index 000000000000..22d894a5a739
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigReadOnlyFeaturesTest.kt
@@ -0,0 +1,167 @@
+/*
+ * 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.systemconfig
+
+import android.content.Context
+import android.content.pm.FeatureInfo
+import android.util.ArrayMap
+import android.util.Xml
+
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.SystemConfig
+import com.google.common.truth.Truth.assertThat
+import org.junit.runner.RunWith
+import org.junit.Rule
+
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class SystemConfigReadOnlyFeaturesTest {
+
+ companion object {
+ private const val FEATURE_ONE = "feature.test.1"
+ private const val FEATURE_TWO = "feature.test.2"
+ private const val FEATURE_RUNTIME_AVAILABLE_TEMPLATE =
+ """
+ <permissions>
+ <feature name="%s" />
+ </permissions>
+ """
+ private const val FEATURE_RUNTIME_DISABLED_TEMPLATE =
+ """
+ <permissions>
+ <Disabled-feature name="%s" />
+ </permissions>
+ """
+
+ fun featureInfo(featureName: String) = FeatureInfo().apply { name = featureName }
+ }
+
+ private val context: Context = InstrumentationRegistry.getInstrumentation().context
+
+ @get:Rule
+ val tempFolder = TemporaryFolder(context.filesDir)
+
+ private val injector = TestInjector()
+
+ private var uniqueCounter = 0
+
+ @Test
+ fun empty() {
+ assertFeatures().isEmpty()
+ }
+
+ @Test
+ fun readOnlyEnabled() {
+ addReadOnlyEnabledFeature(FEATURE_ONE)
+ addReadOnlyEnabledFeature(FEATURE_TWO)
+
+ assertFeatures().containsAtLeast(FEATURE_ONE, FEATURE_TWO)
+ }
+
+ @Test
+ fun readOnlyAndRuntimeEnabled() {
+ addReadOnlyEnabledFeature(FEATURE_ONE)
+ addRuntimeEnabledFeature(FEATURE_TWO)
+
+ // No issues with matching availability.
+ assertFeatures().containsAtLeast(FEATURE_ONE, FEATURE_TWO)
+ }
+
+ @Test
+ fun readOnlyEnabledRuntimeDisabled() {
+ addReadOnlyEnabledFeature(FEATURE_ONE)
+ addRuntimeDisabledFeature(FEATURE_ONE)
+
+ // Read-only feature availability should take precedence.
+ assertFeatures().contains(FEATURE_ONE)
+ }
+
+ @Test
+ fun readOnlyDisabled() {
+ addReadOnlyDisabledFeature(FEATURE_ONE)
+
+ assertFeatures().doesNotContain(FEATURE_ONE)
+ }
+
+ @Test
+ fun readOnlyAndRuntimeDisabled() {
+ addReadOnlyDisabledFeature(FEATURE_ONE)
+ addRuntimeDisabledFeature(FEATURE_ONE)
+
+ // No issues with matching (un)availability.
+ assertFeatures().doesNotContain(FEATURE_ONE)
+ }
+
+ @Test
+ fun readOnlyDisabledRuntimeEnabled() {
+ addReadOnlyDisabledFeature(FEATURE_ONE)
+ addRuntimeEnabledFeature(FEATURE_ONE)
+ addRuntimeEnabledFeature(FEATURE_TWO)
+
+ // Read-only feature (un)availability should take precedence.
+ assertFeatures().doesNotContain(FEATURE_ONE)
+ assertFeatures().contains(FEATURE_TWO)
+ }
+
+ fun addReadOnlyEnabledFeature(featureName: String) {
+ injector.readOnlyEnabledFeatures[featureName] = featureInfo(featureName)
+ }
+
+ fun addReadOnlyDisabledFeature(featureName: String) {
+ injector.readOnlyDisabledFeatures.add(featureName)
+ }
+
+ fun addRuntimeEnabledFeature(featureName: String) {
+ FEATURE_RUNTIME_AVAILABLE_TEMPLATE.format(featureName).write()
+ }
+
+ fun addRuntimeDisabledFeature(featureName: String) {
+ FEATURE_RUNTIME_DISABLED_TEMPLATE.format(featureName).write()
+ }
+
+ private fun String.write() = tempFolder.root.resolve("${uniqueCounter++}.xml")
+ .writeText(this.trimIndent())
+
+ private fun assertFeatures() = assertThat(availableFeatures().keys)
+
+ private fun availableFeatures() = SystemConfig(false, injector).apply {
+ val parser = Xml.newPullParser()
+ readPermissions(parser, tempFolder.root, /*Grant all permission flags*/ 0.inv())
+ }.let { it.availableFeatures }
+
+ internal class TestInjector() : SystemConfig.Injector() {
+ val readOnlyEnabledFeatures = ArrayMap<String, FeatureInfo>()
+ val readOnlyDisabledFeatures = mutableSetOf<String>()
+
+ override fun isReadOnlySystemEnabledFeature(featureName: String, version: Int): Boolean {
+ return readOnlyEnabledFeatures.containsKey(featureName)
+ }
+
+ override fun isReadOnlySystemDisabledFeature(featureName: String, version: Int): Boolean {
+ return readOnlyDisabledFeatures.contains(featureName)
+ }
+
+ override fun getReadOnlySystemEnabledFeatures(): ArrayMap<String, FeatureInfo> {
+ return readOnlyEnabledFeatures
+ }
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index c247c08c8010..3b0cb4ad8779 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -68,6 +68,7 @@ import static org.testng.Assert.assertThrows;
import android.Manifest;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.Flags;
import android.app.IOnProjectionStateChangedListener;
@@ -247,6 +248,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase {
mInjector = spy(new TestInjector());
mUiManagerService = new UiModeManagerService(mContext, /* setupWizardComplete= */ true,
mTwilightManager, mInjector);
+ // Initialize the current user.
+ mUiManagerService.setCurrentUser(ActivityManager.getCurrentUser());
try {
mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
} catch (SecurityException e) {/* ignore for permission denial */}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index becb9fdabfcf..e845d80b412a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4838,7 +4838,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
null, mPkg, Process.myUserHandle());
verify(mPreferencesHelper, times(1)).getNotificationChannels(
- anyString(), anyInt(), anyBoolean());
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
}
@Test
@@ -4856,7 +4856,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
verify(mPreferencesHelper, never()).getNotificationChannels(
- anyString(), anyInt(), anyBoolean());
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
}
@Test
@@ -4871,7 +4871,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
null, mPkg, Process.myUserHandle());
verify(mPreferencesHelper, times(1)).getNotificationChannels(
- anyString(), anyInt(), anyBoolean());
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
}
@Test
@@ -4891,7 +4891,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
verify(mPreferencesHelper, never()).getNotificationChannels(
- anyString(), anyInt(), anyBoolean());
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
}
@Test
@@ -4913,7 +4913,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
verify(mPreferencesHelper, never()).getNotificationChannels(
- anyString(), anyInt(), anyBoolean());
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
}
@Test
@@ -17062,4 +17062,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
assertThat(mService.hasFlag(captor.getValue().getNotification().flags,
FLAG_PROMOTED_ONGOING)).isFalse();
}
+
+ @Test
+ @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testAppCannotUseReservedBundleChannels() throws Exception {
+ mBinderService.getBubblePreferenceForPackage(mPkg, mUid);
+ NotificationChannel news = mBinderService.getNotificationChannel(
+ mPkg, mContext.getUserId(), mPkg, NEWS_ID);
+ assertThat(news).isNotNull();
+
+ NotificationRecord nr = generateNotificationRecord(news);
+ mBinderService.enqueueNotificationWithTag(mPkg, mPkg, nr.getSbn().getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ assertThat(mService.mNotificationList).isEmpty();
+ }
}
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 404ede676f02..b92bdb5f3e6e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -2547,7 +2547,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// Returns only non-deleted channels
List<NotificationChannel> channels =
- mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList();
+ mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true).getList();
// Default channel + non-deleted channel + system defaults
assertEquals(notificationClassification() ? 6 : 2, channels.size());
for (NotificationChannel nc : channels) {
@@ -2557,7 +2557,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
// Returns deleted channels too
- channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList();
+ channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true).getList();
// Includes system channel(s)
assertEquals(notificationClassification() ? 7 : 3, channels.size());
for (NotificationChannel nc : channels) {
@@ -3199,7 +3199,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.permanentlyDeleteNotificationChannels(PKG_N_MR1, UID_N_MR1);
// Only default channel remains
- assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList().size());
+ assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true)
+ .getList().size());
}
@Test
@@ -3315,12 +3316,12 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// user 0 records remain
for (int i = 0; i < user0Uids.length; i++) {
assertEquals(notificationClassification() ? 5 : 1,
- mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false)
+ mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false, true)
.getList().size());
}
// user 1 records are gone
for (int i = 0; i < user1Uids.length; i++) {
- assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false)
+ assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false, true)
.getList().size());
}
}
@@ -3337,7 +3338,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
new int[]{UID_N_MR1}));
assertEquals(0, mHelper.getNotificationChannels(
- PKG_N_MR1, UID_N_MR1, true).getList().size());
+ PKG_N_MR1, UID_N_MR1, true, true).getList().size());
// Not deleted
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false,
@@ -3346,7 +3347,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertFalse(mHelper.onPackagesChanged(false, USER_SYSTEM,
new String[]{PKG_N_MR1}, new int[]{UID_N_MR1}));
assertEquals(notificationClassification() ? 6 : 2,
- mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
+ mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true)
+ .getList().size());
}
@Test
@@ -3405,7 +3407,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertTrue(mHelper.canShowBadge(PKG_O, UID_O));
assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
- assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true).getList().size());
+ assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size());
assertEquals(0, mHelper.getNotificationChannelGroups(PKG_O, UID_O).size());
NotificationChannel channel = getChannel();
@@ -3419,7 +3421,8 @@ public class PreferencesHelperTest extends UiServiceTestCase {
public void testRecordDefaults() throws Exception {
assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
assertEquals(notificationClassification() ? 5 : 1,
- mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
+ mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true)
+ .getList().size());
}
@Test
@@ -6363,6 +6366,15 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
@EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testGetNotificationChannels_omitBundleChannels() {
+ // do something that triggers settings creation for an app
+ mHelper.setShowBadge(PKG_O, UID_O, true);
+
+ assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, false).getList()).isEmpty();
+ }
+
+ @Test
+ @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testNotificationBundles() {
// do something that triggers settings creation for an app
mHelper.setShowBadge(PKG_O, UID_O, true);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index b7821623855c..7f5da41bdf10 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -16,8 +16,6 @@
package com.android.server.vibrator;
-import static android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED;
-
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
@@ -28,6 +26,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -89,17 +88,14 @@ import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
-import android.view.flags.Flags;
import androidx.test.InstrumentationRegistry;
@@ -168,9 +164,7 @@ public class VibratorManagerServiceTest {
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock
private VibratorManagerService.NativeWrapper mNativeWrapperMock;
@@ -800,7 +794,7 @@ public class VibratorManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_CANCEL_BY_APPOPS)
+ @EnableFlags(android.os.vibrator.Flags.FLAG_CANCEL_BY_APPOPS)
public void vibrate_thenDeniedAppOps_getsCancelled() throws Throwable {
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
@@ -894,7 +888,7 @@ public class VibratorManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS)
+ @EnableFlags(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS)
public void vibrate_thenFgUserRequestsMute_getsCancelled() throws Throwable {
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
@@ -1331,6 +1325,37 @@ public class VibratorManagerServiceTest {
}
@Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VIBRATION_PIPELINE_ENABLED)
+ public void vibrate_withPipelineFlagEnabledAndShortEffect_continuesOngoingEffect()
+ throws Exception {
+ assumeTrue(mVibrationConfig.getVibrationPipelineMaxDurationMs() > 0);
+
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ fakeVibrator.setSupportedPrimitives(
+ VibrationEffect.Composition.PRIMITIVE_CLICK,
+ VibrationEffect.Composition.PRIMITIVE_THUD);
+ fakeVibrator.setPrimitiveDuration(
+ mVibrationConfig.getVibrationPipelineMaxDurationMs() - 1);
+ VibratorManagerService service = createSystemReadyService();
+
+ HalVibration firstVibration = vibrateWithUid(service, /* uid= */ 123,
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .compose(), HAPTIC_FEEDBACK_ATTRS);
+ HalVibration secondVibration = vibrateWithUid(service, /* uid= */ 456,
+ VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
+ .compose(), HAPTIC_FEEDBACK_ATTRS);
+ secondVibration.waitForEnd();
+
+ assertThat(fakeVibrator.getAllEffectSegments()).hasSize(2);
+ assertThat(firstVibration.getStatus()).isEqualTo(Status.FINISHED);
+ assertThat(secondVibration.getStatus()).isEqualTo(Status.FINISHED);
+ }
+
+ @Test
public void vibrate_withInputDevices_vibratesInputDevices() throws Exception {
mockVibrators(1);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
@@ -1512,6 +1537,7 @@ public class VibratorManagerServiceTest {
}
@Test
+ @EnableFlags(android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API)
public void performHapticFeedback_doesNotRequireVibrateOrBypassPermissions() throws Exception {
// Deny permissions that would have been required for regular vibrations, and check that
// the vibration proceed as expected to verify that haptic feedback does not need these
@@ -1520,8 +1546,6 @@ public class VibratorManagerServiceTest {
denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
- // Flag override to enable the scroll feedack constants to bypass interruption policies.
- mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
mHapticFeedbackVibrationMap.put(
HapticFeedbackConstants.SCROLL_TICK,
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
@@ -1544,6 +1568,10 @@ public class VibratorManagerServiceTest {
}
@Test
+ @EnableFlags({
+ android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API,
+ android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED,
+ })
public void performHapticFeedbackForInputDevice_doesNotRequireVibrateOrBypassPermissions()
throws Exception {
// Deny permissions that would have been required for regular vibrations, and check that
@@ -1553,9 +1581,6 @@ public class VibratorManagerServiceTest {
denyPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS);
denyPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
denyPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
- // Flag override to enable the scroll feedback constants to bypass interruption policies.
- mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
- mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
mHapticFeedbackVibrationMapSourceRotary.put(
HapticFeedbackConstants.SCROLL_TICK,
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
@@ -1628,12 +1653,14 @@ public class VibratorManagerServiceTest {
}
@Test
+ @EnableFlags({
+ android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API,
+ android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED,
+ })
public void performHapticFeedbackForInputDevice_restrictedConstantsWithoutPermission_doesNotVibrate()
throws Exception {
// Deny permission to vibrate with restricted constants
denyPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS);
- mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
- mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
// Public constant, no permission required
mHapticFeedbackVibrationMapSourceRotary.put(
HapticFeedbackConstants.CONFIRM,
@@ -1697,9 +1724,9 @@ public class VibratorManagerServiceTest {
}
@Test
+ @EnableFlags(android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED)
public void performHapticFeedbackForInputDevice_restrictedConstantsWithPermission_playsVibration()
throws Exception {
- mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
// Grant permission to vibrate with restricted constants
grantPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS);
// Public constant, no permission required
@@ -1732,9 +1759,11 @@ public class VibratorManagerServiceTest {
}
@Test
+ @EnableFlags({
+ android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API,
+ android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED,
+ })
public void performHapticFeedback_doesNotVibrateWhenVibratorInfoNotReady() throws Exception {
- mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
- mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
denyPermission(android.Manifest.permission.VIBRATE);
mHapticFeedbackVibrationMap.put(
HapticFeedbackConstants.KEYBOARD_TAP,
@@ -1767,9 +1796,11 @@ public class VibratorManagerServiceTest {
}
@Test
+ @EnableFlags({
+ android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API,
+ android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED,
+ })
public void performHapticFeedback_doesNotVibrateForInvalidConstant() throws Exception {
- mSetFlagsRule.enableFlags(Flags.FLAG_SCROLL_FEEDBACK_API);
- mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
denyPermission(android.Manifest.permission.VIBRATE);
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
@@ -1791,7 +1822,7 @@ public class VibratorManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
public void vibrate_vendorEffectsWithoutPermission_doesNotVibrate() throws Exception {
// Deny permission to vibrate with vendor effects
denyPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS);
@@ -1816,7 +1847,7 @@ public class VibratorManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
public void vibrate_vendorEffectsWithPermission_successful() throws Exception {
// Grant permission to vibrate with vendor effects
grantPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS);
@@ -1904,7 +1935,7 @@ public class VibratorManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ @EnableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
public void vibrate_withAdaptiveHaptics_appliesCorrectAdaptiveScales() throws Exception {
// Keep user settings the same as device default so only adaptive scale is applied.
setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
@@ -1947,7 +1978,7 @@ public class VibratorManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled({
+ @EnableFlags({
android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED,
android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS,
})
@@ -2418,7 +2449,7 @@ public class VibratorManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ @EnableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
public void onExternalVibration_withAdaptiveHaptics_returnsCorrectAdaptiveScales() {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
@@ -2465,7 +2496,7 @@ public class VibratorManagerServiceTest {
}
@Test
- @RequiresFlagsDisabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ @DisableFlags(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
public void onExternalVibration_withAdaptiveHapticsFlagDisabled_alwaysReturnScaleNone() {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
@@ -2585,7 +2616,7 @@ public class VibratorManagerServiceTest {
}
@Test
- @RequiresFlagsEnabled(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS)
+ @EnableFlags(android.multiuser.Flags.FLAG_ADD_UI_FOR_SOUNDS_FROM_BACKGROUND_USERS)
public void onExternalVibration_thenFgUserRequestsMute_doNotCancelVibration() throws Throwable {
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
@@ -3105,9 +3136,20 @@ public class VibratorManagerServiceTest {
return vibrateWithDevice(service, Context.DEVICE_ID_DEFAULT, effect, attrs);
}
+ private HalVibration vibrateWithUid(VibratorManagerService service, int uid,
+ VibrationEffect effect, VibrationAttributes attrs) {
+ return vibrateWithUidAndDevice(service, uid, Context.DEVICE_ID_DEFAULT,
+ CombinedVibration.createParallel(effect), attrs);
+ }
+
private HalVibration vibrateWithDevice(VibratorManagerService service, int deviceId,
CombinedVibration effect, VibrationAttributes attrs) {
- HalVibration vib = service.vibrateWithPermissionCheck(UID, deviceId, PACKAGE_NAME, effect,
+ return vibrateWithUidAndDevice(service, UID, deviceId, effect, attrs);
+ }
+
+ private HalVibration vibrateWithUidAndDevice(VibratorManagerService service, int uid,
+ int deviceId, CombinedVibration effect, VibrationAttributes attrs) {
+ HalVibration vib = service.vibrateWithPermissionCheck(uid, deviceId, PACKAGE_NAME, effect,
attrs, "some reason", service);
if (vib != null) {
mPendingVibrations.add(vib);
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 6dc1b10ec930..75a9cedfd8c4 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -80,6 +80,7 @@ public final class FakeVibratorControllerProvider {
private float[] mFrequenciesHz;
private float[] mOutputAccelerationsGs;
private long mVendorEffectDuration = EFFECT_DURATION;
+ private long mPrimitiveDuration = EFFECT_DURATION;
void recordEffectSegment(long vibrationId, VibrationEffectSegment segment) {
mEffectSegments.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(segment);
@@ -171,7 +172,7 @@ public final class FakeVibratorControllerProvider {
}
long duration = 0;
for (PrimitiveSegment primitive : primitives) {
- duration += EFFECT_DURATION + primitive.getDelay();
+ duration += mPrimitiveDuration + primitive.getDelay();
recordEffectSegment(vibrationId, primitive);
}
applyLatency(mOnLatency);
@@ -381,6 +382,11 @@ public final class FakeVibratorControllerProvider {
mVendorEffectDuration = durationMs;
}
+ /** Set the duration of primitives in fake vibrator hardware. */
+ public void setPrimitiveDuration(long primitiveDuration) {
+ mPrimitiveDuration = primitiveDuration;
+ }
+
/**
* Set the maximum number of envelope effects control points supported in fake vibrator
* hardware.
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index ab00bfdf41ae..1f167761fc06 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -71,6 +71,7 @@ android_test {
"CtsSurfaceValidatorLib",
"service-sdksandbox.impl",
"com.android.window.flags.window-aconfig-java",
+ "android.view.inputmethod.flags-aconfig-java",
"flag-junit",
],
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index d0080d29f82b..d5f86b6feac8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1261,6 +1261,33 @@ public class ActivityStarterTests extends WindowTestsBase {
}
@Test
+ public void testLaunchAdjacentDisabled() {
+ final ActivityStarter starter =
+ prepareStarter(FLAG_ACTIVITY_LAUNCH_ADJACENT, false /* mockGetRootTask */);
+ final ActivityRecord top = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ final ActivityRecord[] outActivity = new ActivityRecord[1];
+
+ // Activity must not land on split-screen task if currently not in split-screen mode.
+ starter.setActivityOptions(options.toBundle())
+ .setReason("testLaunchAdjacentDisabled")
+ .setOutActivity(outActivity).execute();
+ assertThat(outActivity[0].inMultiWindowMode()).isFalse();
+
+ // Move activity to split-screen-primary task and make sure it has the focus.
+ TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm, top.getDisplayContent());
+ top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM);
+ top.getRootTask().moveToFront("testLaunchAdjacentDisabled");
+ top.getRootTask().setLaunchAdjacentDisabled(true);
+
+ // Ensure activity does not launch into split-screen-secondary when launch adjacent is
+ // disabled
+ startActivityInner(starter, outActivity[0], top, options, null /* inTask */,
+ null /* taskFragment*/);
+ assertThat(outActivity[0].isDescendantOf(splitOrg.mSecondary)).isFalse();
+ }
+
+ @Test
public void testTransientLaunchWithKeyguard() {
final ActivityStarter starter = prepareStarter(0 /* flags */);
final ActivityRecord target = new ActivityBuilder(mAtm).setCreateTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index d2cf03dd4b9a..ee56210e278d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -41,11 +41,13 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.verify;
import android.app.StatusBarManager;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Binder;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.view.InsetsFrameProvider;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
@@ -525,6 +527,59 @@ public class InsetsPolicyTest extends WindowTestsBase {
assertTrue(win1.getWindowFrames().hasInsetsChanged());
}
+ /**
+ * This test verifies that after setting {@link WindowContainer#mExcludeInsetsTypes}, the IME
+ * insets have a height of zero (applied in {@link InsetsPolicy#adjustVisibilityForIme}).
+ */
+ @RequiresFlagsEnabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
+ @SetupWindows(addWindows = W_INPUT_METHOD)
+ @Test
+ public void testExcludeImeInsets() {
+ final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+ final InsetsSource imeSource = new InsetsSource(ID_IME, ime());
+ imeSource.setVisible(true);
+ mImeWindow.mHasSurface = true;
+
+ final WindowState win = addWindow(TYPE_APPLICATION, "win1");
+ win.setRequestedVisibleTypes(0, ime());
+
+ win.mAboveInsetsState.addSource(imeSource);
+ win.mHasSurface = true;
+
+ DisplayContentTests.performLayout(mDisplayContent);
+ // IME should cover half of the app's window
+ final var winFrame = win.getFrame();
+ imeSource.setFrame(winFrame.left, winFrame.bottom / 2, winFrame.right, winFrame.bottom);
+ imeSource.setVisibleFrame(imeSource.getFrame());
+ DisplayContentTests.performLayout(mDisplayContent);
+
+ assertTrue(mImeWindow.isVisible());
+ assertTrue(win.isVisible());
+
+ displayPolicy.beginPostLayoutPolicyLw();
+ displayPolicy.applyPostLayoutPolicyLw(win, win.mAttrs, null, null);
+ displayPolicy.finishPostLayoutPolicyLw();
+
+ final var imeInsetsShown = win.getInsetsState().calculateInsets(win.getFrame(), ime(),
+ true);
+ assertEquals(new Rect(0, 0, 0, winFrame.bottom / 2), imeInsetsShown.toRect());
+
+
+ // Now we're setting the excludedInsetsTypes for the IME. The IME is still showing, but
+ // in this case, InsetsPolicy#adjustVisibilityForIme will override and dispatch IME
+ // insets with zero height.
+ win.setExcludeInsetsTypes(ime());
+
+ displayPolicy.beginPostLayoutPolicyLw();
+ displayPolicy.applyPostLayoutPolicyLw(win, win.mAttrs, null, null);
+ displayPolicy.finishPostLayoutPolicyLw();
+
+ final var imeInsetsHidden = win.getInsetsState().calculateInsets(win.getFrame(), ime(),
+ true);
+ assertEquals(Insets.NONE, imeInsetsHidden);
+ }
+
+
private WindowState addNavigationBar() {
final Binder owner = new Binder();
final WindowState win = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 457058849fca..79967b861ea5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -30,6 +30,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.graphics.Insets;
+import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.InsetsSource;
@@ -260,6 +261,27 @@ public class InsetsSourceProviderTest extends WindowTestsBase {
}
@Test
+ public void testUpdateInsetsControlPosition() {
+ final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
+
+ final WindowState ime1 = createWindow(null, TYPE_INPUT_METHOD, "ime1");
+ ime1.getFrame().set(new Rect(0, 0, 0, 0));
+ mImeProvider.setWindowContainer(ime1, null, null);
+ mImeProvider.updateControlForTarget(target, false /* force */, null /* statsToken */);
+ ime1.getFrame().set(new Rect(0, 400, 500, 500));
+ mImeProvider.updateInsetsControlPosition(ime1);
+ assertEquals(new Point(0, 400), mImeProvider.getControl(target).getSurfacePosition());
+
+ final WindowState ime2 = createWindow(null, TYPE_INPUT_METHOD, "ime2");
+ ime2.getFrame().set(new Rect(0, 0, 0, 0));
+ mImeProvider.setWindowContainer(ime2, null, null);
+ mImeProvider.updateControlForTarget(target, false /* force */, null /* statsToken */);
+ ime2.getFrame().set(new Rect(0, 400, 500, 500));
+ mImeProvider.updateInsetsControlPosition(ime2);
+ assertEquals(new Point(0, 400), mImeProvider.getControl(target).getSurfacePosition());
+ }
+
+ @Test
public void testSetRequestedVisibleTypes() {
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 72f4fa9158fb..c30f70ee2903 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -183,15 +183,32 @@ public class SizeCompatTests extends WindowTestsBase {
DeviceConfig.setProperties(mInitialConstrainDisplayApisFlags);
}
- private void setUpApp(DisplayContent display) {
- mTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true).build();
- mActivity = mTask.getTopNonFinishingActivity();
+ private ActivityRecord setUpApp(DisplayContent display) {
+ return setUpApp(display, null /* appBuilder */);
+ }
+
+ private ActivityRecord setUpApp(DisplayContent display, ActivityBuilder appBuilder) {
+ // Use the real package name (com.android.frameworks.wmtests) so that
+ // EnableCompatChanges/DisableCompatChanges can take effect.
+ // Otherwise the fake WindowTestsBase.DEFAULT_COMPONENT_PACKAGE_NAME will make
+ // PlatformCompat#isChangeEnabledByPackageName always return default value.
+ final ComponentName componentName = ComponentName.createRelative(
+ mContext, SizeCompatTests.class.getName());
+ mTask = new TaskBuilder(mSupervisor).setDisplay(display).setComponent(componentName)
+ .build();
+ final ActivityBuilder builder = appBuilder != null ? appBuilder : new ActivityBuilder(mAtm);
+ mActivity = builder.setTask(mTask).setComponent(componentName).build();
doReturn(false).when(mActivity).isImmersiveMode(any());
+ return mActivity;
+ }
+
+ private ActivityRecord setUpDisplaySizeWithApp(int dw, int dh) {
+ return setUpDisplaySizeWithApp(dw, dh, null /* appBuilder */);
}
- private void setUpDisplaySizeWithApp(int dw, int dh) {
+ private ActivityRecord setUpDisplaySizeWithApp(int dw, int dh, ActivityBuilder appBuilder) {
final TestDisplayContent.Builder builder = new TestDisplayContent.Builder(mAtm, dw, dh);
- setUpApp(builder.build());
+ return setUpApp(builder.build(), appBuilder);
}
@Test
@@ -352,15 +369,13 @@ public class SizeCompatTests extends WindowTestsBase {
.setCanRotate(false)
.setNotch(cutoutHeight)
.build();
- setUpApp(display);
display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
mWm.mAppCompatConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
mWm.mAppCompatConfiguration.setIsVerticalReachabilityEnabled(true);
- final ActivityRecord activity = getActivityBuilderOnSameTask()
+ final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
.setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
- .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
- .build();
+ .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE));
doReturn(true).when(activity).isImmersiveMode(any());
addWindowToActivity(activity);
@@ -424,17 +439,16 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
@DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
public void testFixedAspectRatioBoundsWithDecorInSquareDisplay() {
- final int notchHeight = 100;
- setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800).setNotch(notchHeight).build());
-
- final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds();
final float aspectRatio = 1.2f;
- final ActivityRecord activity = getActivityBuilderOnSameTask()
+ final ActivityBuilder activityBuilder = new ActivityBuilder(mAtm)
.setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
.setMinAspectRatio(aspectRatio)
.setMaxAspectRatio(aspectRatio)
- .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
- .build();
+ .setResizeMode(RESIZE_MODE_UNRESIZEABLE);
+ final int notchHeight = 100;
+ final ActivityRecord activity = setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800)
+ .setNotch(notchHeight).build(), activityBuilder);
+ final Rect displayBounds = activity.mDisplayContent.getWindowConfiguration().getBounds();
final Rect appBounds = activity.getWindowConfiguration().getAppBounds();
// The parent configuration doesn't change since the first resolved configuration, so the
@@ -1327,12 +1341,8 @@ public class SizeCompatTests extends WindowTestsBase {
public void testOverrideMinAspectRatioSmall_overridden() {
final int dh = 1200;
final int dw = 1000;
- setUpDisplaySizeWithApp(dw, dh);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(dw, dh, new ActivityBuilder(mAtm)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
final Rect bounds = activity.getBounds();
assertEquals(dh, bounds.height());
@@ -1346,13 +1356,9 @@ public class SizeCompatTests extends WindowTestsBase {
public void testOverrideMinAspectRatioSmall_notOverridden() {
final int dh = 1200;
final int dw = 1000;
- setUpDisplaySizeWithApp(dw, dh);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(dw, dh, new ActivityBuilder(mAtm)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+ .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE));
// Activity's requested aspect ratio is larger than OVERRIDE_MIN_ASPECT_RATIO_SMALL,
// so OVERRIDE_MIN_ASPECT_RATIO_SMALL is ignored.
@@ -1366,12 +1372,8 @@ public class SizeCompatTests extends WindowTestsBase {
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioMedium() {
- setUpDisplaySizeWithApp(1000, 1200);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+ new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
// The per-package override forces the activity into a 3:2 aspect ratio
assertEquals(1200, activity.getBounds().height());
@@ -1388,13 +1390,8 @@ public class SizeCompatTests extends WindowTestsBase {
final int notchHeight = 200;
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, dh)
.setNotch(notchHeight).setSystemDecorations(true).build();
- mTask = new TaskBuilder(mSupervisor).setDisplay(display).build();
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .setMinAspectRatio(2f)
- .build();
+ final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).setMinAspectRatio(2f));
// The per-package override should have no effect, because the manifest aspect ratio is
// larger (2:1)
@@ -1411,13 +1408,9 @@ public class SizeCompatTests extends WindowTestsBase {
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
public void testOverrideMinAspectRatioLargerThanManifest() {
- setUpDisplaySizeWithApp(1400, 1600);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .setMinAspectRatio(1.1f)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(1400, 1600,
+ new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+ .setMinAspectRatio(1.1f));
// The per-package override should have no effect, because the manifest aspect ratio is
// larger (2:1)
@@ -1430,12 +1423,8 @@ public class SizeCompatTests extends WindowTestsBase {
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
public void testOverrideMinAspectRatioLarge() {
- setUpDisplaySizeWithApp(1500, 1600);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(1500, 1600,
+ new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
// The per-package override forces the activity into a 16:9 aspect ratio
assertEquals(1600, activity.getBounds().height());
@@ -1449,13 +1438,8 @@ public class SizeCompatTests extends WindowTestsBase {
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
public void testOverrideMinAspectRatio_Both() {
// If multiple override aspect ratios are set, we should use the largest one
-
- setUpDisplaySizeWithApp(1400, 1600);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(1400, 1600,
+ new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
// The per-package override forces the activity into a 16:9 aspect ratio
assertEquals(1600, activity.getBounds().height());
@@ -1470,11 +1454,7 @@ public class SizeCompatTests extends WindowTestsBase {
public void testOverrideMinAspectRatioScreenOrientationNotSetThenChangedToPortrait() {
// In this test, the activity's orientation isn't fixed to portrait, therefore the override
// isn't applied.
-
- setUpDisplaySizeWithApp(1000, 1200);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200);
// The per-package override should have no effect
assertEquals(1200, activity.getBounds().height());
@@ -1497,13 +1477,8 @@ public class SizeCompatTests extends WindowTestsBase {
public void testOverrideMinAspectRatioScreenOrientationLandscapeThenChangedToPortrait() {
// In this test, the activity's orientation isn't fixed to portrait, therefore the override
// isn't applied.
-
- setUpDisplaySizeWithApp(1000, 1200);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+ new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE));
// The per-package override should have no effect
assertEquals(1200, activity.getBounds().height());
@@ -1524,12 +1499,8 @@ public class SizeCompatTests extends WindowTestsBase {
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
public void testOverrideMinAspectRatioScreenOrientationPortraitThenChangedToUnspecified() {
- setUpDisplaySizeWithApp(1000, 1200);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+ new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
// The per-package override forces the activity into a 3:2 aspect ratio
assertEquals(1200, activity.getBounds().height());
@@ -1550,10 +1521,7 @@ public class SizeCompatTests extends WindowTestsBase {
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
@DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY})
public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationNotSet() {
- setUpDisplaySizeWithApp(1000, 1200);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200);
// The per-package override forces the activity into a 3:2 aspect ratio
assertEquals(1200, activity.getBounds().height());
@@ -1568,13 +1536,8 @@ public class SizeCompatTests extends WindowTestsBase {
public void testOverrideMinAspectRatioPortraitOnlyDisabledScreenOrientationLandscape() {
// In this test, the activity's orientation isn't fixed to portrait, therefore the override
// isn't applied.
-
- setUpDisplaySizeWithApp(1000, 1200);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+ new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE));
// The per-package override forces the activity into a 3:2 aspect ratio
assertEquals(1000 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
@@ -1588,12 +1551,8 @@ public class SizeCompatTests extends WindowTestsBase {
// In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without
// OVERRIDE_MIN_ASPECT_RATIO being also set.
- setUpDisplaySizeWithApp(1000, 1200);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(1000, 1200,
+ new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE));
// The per-package override should have no effect
assertEquals(1200, activity.getBounds().height());
@@ -1604,12 +1563,8 @@ public class SizeCompatTests extends WindowTestsBase {
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
public void testOverrideMinAspectRatioLargeForResizableAppInSplitScreen() {
- setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800,
+ new ActivityBuilder(mAtm).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
final TestSplitOrganizer organizer =
new TestSplitOrganizer(mAtm, activity.getDisplayContent());
@@ -2462,10 +2417,8 @@ public class SizeCompatTests extends WindowTestsBase {
public void testOverrideSplitScreenAspectRatioForUnresizablePortraitApps() {
final int displayWidth = 1400;
final int displayHeight = 1600;
- setUpDisplaySizeWithApp(displayWidth, displayHeight);
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setMinAspectRatio(1.1f)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight,
+ new ActivityBuilder(mAtm).setMinAspectRatio(1.1f));
// Setup Letterbox Configuration
activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
@@ -2483,10 +2436,8 @@ public class SizeCompatTests extends WindowTestsBase {
public void testOverrideSplitScreenAspectRatioForUnresizablePortraitAppsFromLandscape() {
final int displayWidth = 1600;
final int displayHeight = 1400;
- setUpDisplaySizeWithApp(displayWidth, displayHeight);
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setMinAspectRatio(1.1f)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight,
+ new ActivityBuilder(mAtm).setMinAspectRatio(1.1f));
// Setup Letterbox Configuration
activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
@@ -2505,10 +2456,8 @@ public class SizeCompatTests extends WindowTestsBase {
public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeApps() {
final int displayWidth = 1400;
final int displayHeight = 1600;
- setUpDisplaySizeWithApp(displayWidth, displayHeight);
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setMinAspectRatio(1.1f)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight,
+ new ActivityBuilder(mAtm).setMinAspectRatio(1.1f));
// Setup Letterbox Configuration
activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
@@ -2527,10 +2476,8 @@ public class SizeCompatTests extends WindowTestsBase {
public void testOverrideSplitScreenAspectRatioForUnresizableLandscapeAppsFromLandscape() {
final int displayWidth = 1600;
final int displayHeight = 1400;
- setUpDisplaySizeWithApp(displayWidth, displayHeight);
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setMinAspectRatio(1.1f)
- .build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(displayWidth, displayHeight,
+ new ActivityBuilder(mAtm).setMinAspectRatio(1.1f));
// Setup Letterbox Configuration
activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
activity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.5f);
@@ -2549,8 +2496,7 @@ public class SizeCompatTests extends WindowTestsBase {
mAtm.mDevEnableNonResizableMultiWindow = true;
final int screenWidth = 1800;
final int screenHeight = 1000;
- setUpDisplaySizeWithApp(screenWidth, screenHeight);
- final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(screenWidth, screenHeight);
activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Simulate real display with top insets.
@@ -2585,8 +2531,7 @@ public class SizeCompatTests extends WindowTestsBase {
mAtm.mDevEnableNonResizableMultiWindow = true;
final int screenWidth = 1000;
final int screenHeight = 1800;
- setUpDisplaySizeWithApp(screenWidth, screenHeight);
- final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+ final ActivityRecord activity = setUpDisplaySizeWithApp(screenWidth, screenHeight);
activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Simulate real display with top insets.
@@ -2616,18 +2561,18 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN})
public void testOverrideMinAspectRatioExcludePortraitFullscreen() {
- setUpDisplaySizeWithApp(2600, 1600);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
- mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
-
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+ resizeDisplay(mDisplayContent, 2600, 1600);
+ mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
// Non-resizable portrait activity
- prepareUnresizable(activity, 0f, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ final ActivityRecord activity = setUpApp(mDisplayContent, new ActivityBuilder(mAtm)
+ .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
// At first, OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_FULLSCREEN does not apply, because the
// display is in landscape
@@ -2645,16 +2590,16 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE,
ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN})
public void testOverrideMinAspectRatioExcludePortraitFullscreenNotApplied() {
// In this test, the activity is not in fullscreen, so the override is not applied
- setUpDisplaySizeWithApp(2600, 1600);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
- mActivity.mWmService.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
+ resizeDisplay(mDisplayContent, 2600, 1600);
+ mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mWm.mAppCompatConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
- // Create a size compat activity on the same task.
- final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+ final ActivityRecord activity = setUpApp(mDisplayContent);
final TestSplitOrganizer organizer =
new TestSplitOrganizer(mAtm, activity.getDisplayContent());
@@ -2983,6 +2928,7 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION})
public void testDisplayIgnoreOrientationRequest_orientationChangedToUnspecified() {
// Set up a display in landscape and ignoring orientation request.
setUpDisplaySizeWithApp(2800, 1400);
@@ -3437,6 +3383,7 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION})
public void testSupportsNonResizableInSplitScreen_letterboxForAspectRatioRestriction() {
// Support non resizable in multi window
mAtm.mDevEnableNonResizableMultiWindow = true;
@@ -4050,9 +3997,9 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION})
public void testPortraitCloseToSquareDisplayWithTaskbar_insetsOverridden_notLetterboxed() {
+ final DisplayContent display = mDisplayContent;
// Set up portrait close to square display.
- setUpDisplaySizeWithApp(2200, 2280);
- final DisplayContent display = mActivity.mDisplayContent;
+ resizeDisplay(display, 2200, 2280);
display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Simulate insets, final app bounds are (0, 0, 2200, 2130) - landscape.
final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
@@ -4067,9 +4014,8 @@ public class SizeCompatTests extends WindowTestsBase {
display.sendNewConfiguration();
}
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
- .build();
+ final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
// Activity should not be letterboxed and should have portrait app bounds even though
// orientation is not respected with insets as insets have been decoupled.
@@ -4085,9 +4031,9 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
@DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
public void testPortraitCloseToSquareDisplayWithTaskbar_letterboxed() {
+ final DisplayContent display = mDisplayContent;
// Set up portrait close to square display
- setUpDisplaySizeWithApp(2200, 2280);
- final DisplayContent display = mActivity.mDisplayContent;
+ resizeDisplay(display, 2200, 2280);
display.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape
final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
@@ -4102,9 +4048,8 @@ public class SizeCompatTests extends WindowTestsBase {
display.sendNewConfiguration();
}
- final ActivityRecord activity = getActivityBuilderOnSameTask()
- .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
- .build();
+ final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
+ .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT));
final Rect bounds = activity.getBounds();
// Activity should be letterboxed and should have portrait app bounds
@@ -4116,9 +4061,9 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
@DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
public void testFixedAspectRatioAppInPortraitCloseToSquareDisplay_notInSizeCompat() {
- setUpDisplaySizeWithApp(2200, 2280);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
- final DisplayContent dc = mActivity.mDisplayContent;
+ final DisplayContent dc = mDisplayContent;
+ resizeDisplay(dc, 2200, 2280);
+ dc.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
// Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape
final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
"navbar");
@@ -4132,11 +4077,10 @@ public class SizeCompatTests extends WindowTestsBase {
dc.sendNewConfiguration();
}
- final ActivityRecord activity = getActivityBuilderOnSameTask()
+ final ActivityRecord activity = setUpApp(dc, new ActivityBuilder(mAtm)
.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
.setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
- .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
- .build();
+ .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE));
// To force config to update again but with the same landscape orientation.
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
@@ -4174,13 +4118,10 @@ public class SizeCompatTests extends WindowTestsBase {
.setCutout(0, 100, 0, 150)
.build();
- setUpApp(display);
-
- final ActivityRecord activity = getActivityBuilderOnSameTask()
+ final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
.setMaxAspectRatio(2.1f)
- .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
- .build();
+ .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
// The activity height is 2100 and the display's app bounds height is 2050, so the activity
// cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
@@ -4196,13 +4137,10 @@ public class SizeCompatTests extends WindowTestsBase {
.setCutout(100, 0, 150, 0)
.build();
- setUpApp(display);
-
- final ActivityRecord activity = getActivityBuilderOnSameTask()
+ final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
.setMaxAspectRatio(2.1f)
- .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
- .build();
+ .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
// The activity width is 2100 and the display's app bounds width is 2250, so the activity
// can be aligned inside parentAppBounds
assertEquals(activity.getBounds(), new Rect(175, 0, 2275, 1000));
@@ -4216,12 +4154,10 @@ public class SizeCompatTests extends WindowTestsBase {
.setCutout(100, 0, 150, 0)
.build();
- setUpApp(display);
- final ActivityRecord activity = getActivityBuilderOnSameTask()
+ final ActivityRecord activity = setUpApp(display, new ActivityBuilder(mAtm)
.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
.setMaxAspectRatio(2.1f)
- .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
- .build();
+ .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED));
// The activity width is 2100 and the display's app bounds width is 2050, so the activity
// cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
assertEquals(activity.getBounds(), display.getBounds());
@@ -4883,7 +4819,7 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
- @EnableCompatChanges({ActivityRecord.UNIVERSAL_RESIZABLE_BY_DEFAULT})
+ @EnableCompatChanges({ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT})
public void testUniversalResizeableByDefault() {
mSetFlagsRule.enableFlags(Flags.FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT);
mDisplayContent.setIgnoreOrientationRequest(false);
@@ -5032,17 +4968,13 @@ public class SizeCompatTests extends WindowTestsBase {
*/
private ActivityRecord buildActivityRecord(boolean supportsSizeChanges, int resizeMode,
@ScreenOrientation int screenOrientation) {
- return getActivityBuilderOnSameTask()
+ return getActivityBuilderWithoutTask().setTask(mTask)
.setResizeMode(resizeMode)
.setSupportsSizeChanges(supportsSizeChanges)
.setScreenOrientation(screenOrientation)
.build();
}
- private ActivityBuilder getActivityBuilderOnSameTask() {
- return getActivityBuilderWithoutTask().setTask(mTask);
- }
-
private ActivityBuilder getActivityBuilderWithoutTask() {
return new ActivityBuilder(mAtm)
.setComponent(ComponentName.createRelative(mContext,
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c8aa61c0316d..a7fe0cb0940c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2127,7 +2127,6 @@ public class TelephonyManager {
* <p>On some devices, this settings activity may not exist. Callers should ensure that this
* case is appropriately handled.
*/
- @FlaggedApi(Flags.FLAG_RESET_MOBILE_NETWORK_SETTINGS)
@SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS =
"android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS";
@@ -8779,13 +8778,14 @@ public class TelephonyManager {
* Authentication error, no memory space available in EFMUK
*
* @throws UnsupportedOperationException If the device does not have
- * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION} or doesn't support given
+ * authType.
*/
// TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
// READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
// it's not public API.
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
- public String getIccAuthentication(int appType,@AuthType int authType, String data) {
+ public String getIccAuthentication(int appType, @AuthType int authType, String data) {
return getIccAuthentication(getSubId(), appType, authType, data);
}
@@ -8810,10 +8810,14 @@ public class TelephonyManager {
* Key freshness failure
* Authentication error, no memory space available
* Authentication error, no memory space available in EFMUK
+ * @throws UnsupportedOperationException If the device does not have
+ * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION} or doesn't support given
+ * authType.
* @hide
*/
@UnsupportedAppUsage
- public String getIccAuthentication(int subId, int appType,@AuthType int authType, String data) {
+ public String getIccAuthentication(int subId, int appType, @AuthType int authType,
+ String data) {
try {
IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index f2e34257ef01..9ce8e807f612 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -352,7 +352,8 @@
android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenActivity"
android:theme="@style/CutoutShortEdges"
android:label="SplitScreenPrimaryActivity"
- android:exported="true">
+ android:exported="true"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -363,7 +364,8 @@
android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenSecondaryActivity"
android:theme="@style/CutoutShortEdges"
android:label="SplitScreenSecondaryActivity"
- android:exported="true">
+ android:exported="true"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
diff --git a/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
index 90dff47ab706..a1e165551b5b 100644
--- a/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
@@ -24,17 +24,16 @@ import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import androidx.test.core.app.ApplicationProvider
import com.android.server.testutils.any
+import com.android.test.input.MockInputManagerRule
import java.util.concurrent.Executor
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import kotlin.test.fail
-import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doAnswer
@@ -61,9 +60,8 @@ class InputDeviceBatteryListenerTest {
private lateinit var context: Context
private lateinit var inputManager: InputManager
- @Mock
- private lateinit var iInputManagerMock: IInputManager
- private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+ @get:Rule
+ val inputManagerRule = MockInputManagerRule()
@Before
fun setUp() {
@@ -72,7 +70,6 @@ class InputDeviceBatteryListenerTest {
executor = HandlerExecutor(Handler(testLooper.looper))
registeredListener = null
monitoredDevices.clear()
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
inputManager = InputManager(context)
`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
.thenReturn(inputManager)
@@ -92,7 +89,7 @@ class InputDeviceBatteryListenerTest {
monitoredDevices.add(deviceId)
registeredListener = listener
null
- }.`when`(iInputManagerMock).registerBatteryListener(anyInt(), any())
+ }.`when`(inputManagerRule.mock).registerBatteryListener(anyInt(), any())
// Handle battery listener being unregistered.
doAnswer {
@@ -108,14 +105,7 @@ class InputDeviceBatteryListenerTest {
if (monitoredDevices.isEmpty()) {
registeredListener = null
}
- }.`when`(iInputManagerMock).unregisterBatteryListener(anyInt(), any())
- }
-
- @After
- fun tearDown() {
- if (this::inputManagerGlobalSession.isInitialized) {
- inputManagerGlobalSession.close()
- }
+ }.`when`(inputManagerRule.mock).unregisterBatteryListener(anyInt(), any())
}
private fun notifyBatteryStateChanged(
diff --git a/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java
index 080186e4a2c1..3fc9ce12e718 100644
--- a/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java
+++ b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java
@@ -45,15 +45,14 @@ import android.view.InputDevice;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.test.input.MockInputManagerRule;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.Arrays;
@@ -73,23 +72,22 @@ public class InputDeviceLightsManagerTest {
private static final int DEVICE_ID = 1000;
private static final int PLAYER_ID = 3;
- @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+ @Rule
+ public final MockInputManagerRule mInputManagerRule = new MockInputManagerRule();
private InputManager mInputManager;
- @Mock private IInputManager mIInputManagerMock;
private InputManagerGlobal.TestSession mInputManagerGlobalSession;
@Before
public void setUp() throws Exception {
final Context context = spy(
new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()));
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
+ when(mInputManagerRule.getMock().getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
- when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
+ when(mInputManagerRule.getMock().getInputDevice(eq(DEVICE_ID))).thenReturn(
createInputDevice(DEVICE_ID));
- mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock);
mInputManager = new InputManager(context);
when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager);
@@ -102,7 +100,7 @@ public class InputDeviceLightsManagerTest {
lightStatesById.put(lightIds[i], lightStates[i]);
}
return null;
- }).when(mIInputManagerMock).setLightStates(eq(DEVICE_ID),
+ }).when(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID),
any(int[].class), any(LightState[].class), any(IBinder.class));
doAnswer(invocation -> {
@@ -111,7 +109,7 @@ public class InputDeviceLightsManagerTest {
return lightStatesById.get(lightId);
}
return new LightState(0);
- }).when(mIInputManagerMock).getLightState(eq(DEVICE_ID), anyInt());
+ }).when(mInputManagerRule.getMock()).getLightState(eq(DEVICE_ID), anyInt());
}
@After
@@ -130,7 +128,7 @@ public class InputDeviceLightsManagerTest {
private void mockLights(Light[] lights) throws Exception {
// Mock the Lights returned form InputManagerService
- when(mIInputManagerMock.getLights(eq(DEVICE_ID))).thenReturn(
+ when(mInputManagerRule.getMock().getLights(eq(DEVICE_ID))).thenReturn(
new ArrayList(Arrays.asList(lights)));
}
@@ -151,7 +149,7 @@ public class InputDeviceLightsManagerTest {
LightsManager lightsManager = device.getLightsManager();
List<Light> lights = lightsManager.getLights();
- verify(mIInputManagerMock).getLights(eq(DEVICE_ID));
+ verify(mInputManagerRule.getMock()).getLights(eq(DEVICE_ID));
assertEquals(lights, Arrays.asList(mockedLights));
}
@@ -185,9 +183,9 @@ public class InputDeviceLightsManagerTest {
.build());
IBinder token = session.getToken();
- verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
+ verify(mInputManagerRule.getMock()).openLightSession(eq(DEVICE_ID),
any(String.class), eq(token));
- verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}),
+ verify(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}),
eq(states), eq(token));
// Then all 3 should turn on.
@@ -204,7 +202,7 @@ public class InputDeviceLightsManagerTest {
// close session
session.close();
- verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
+ verify(mInputManagerRule.getMock()).closeLightSession(eq(DEVICE_ID), eq(token));
}
@Test
@@ -232,9 +230,9 @@ public class InputDeviceLightsManagerTest {
.build());
IBinder token = session.getToken();
- verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
+ verify(mInputManagerRule.getMock()).openLightSession(eq(DEVICE_ID),
any(String.class), eq(token));
- verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1}),
+ verify(mInputManagerRule.getMock()).setLightStates(eq(DEVICE_ID), eq(new int[]{1}),
eq(states), eq(token));
// Verify the light state
@@ -245,7 +243,7 @@ public class InputDeviceLightsManagerTest {
// close session
session.close();
- verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
+ verify(mInputManagerRule.getMock()).closeLightSession(eq(DEVICE_ID), eq(token));
}
@Test
diff --git a/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java
index 0e3c200699d2..3057f5ddb540 100644
--- a/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java
+++ b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java
@@ -41,16 +41,13 @@ import android.view.InputDevice;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.annotations.GuardedBy;
+import com.android.test.input.MockInputManagerRule;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.junit.MockitoRule;
import java.util.List;
import java.util.concurrent.BlockingQueue;
@@ -70,43 +67,34 @@ public class InputDeviceSensorManagerTest {
private static final int DEVICE_ID = 1000;
- @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+ @Rule
+ public final MockInputManagerRule mInputManagerRule = new MockInputManagerRule();
private InputManager mInputManager;
private IInputSensorEventListener mIInputSensorEventListener;
private final Object mLock = new Object();
- @Mock private IInputManager mIInputManagerMock;
- private InputManagerGlobal.TestSession mInputManagerGlobalSession;
-
@Before
public void setUp() throws Exception {
final Context context = spy(
new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()));
- mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock);
mInputManager = new InputManager(context);
when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager);
- when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
+ when(mInputManagerRule.getMock().getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
- when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
+ when(mInputManagerRule.getMock().getInputDevice(eq(DEVICE_ID))).thenReturn(
createInputDeviceWithSensor(DEVICE_ID));
- when(mIInputManagerMock.getSensorList(eq(DEVICE_ID))).thenReturn(new InputSensorInfo[] {
- createInputSensorInfo(DEVICE_ID, Sensor.TYPE_ACCELEROMETER),
- createInputSensorInfo(DEVICE_ID, Sensor.TYPE_GYROSCOPE)});
+ when(mInputManagerRule.getMock().getSensorList(eq(DEVICE_ID))).thenReturn(
+ new InputSensorInfo[]{
+ createInputSensorInfo(DEVICE_ID, Sensor.TYPE_ACCELEROMETER),
+ createInputSensorInfo(DEVICE_ID, Sensor.TYPE_GYROSCOPE)});
- when(mIInputManagerMock.enableSensor(eq(DEVICE_ID), anyInt(), anyInt(), anyInt()))
+ when(mInputManagerRule.getMock().enableSensor(eq(DEVICE_ID), anyInt(), anyInt(), anyInt()))
.thenReturn(true);
- when(mIInputManagerMock.registerSensorListener(any())).thenReturn(true);
- }
-
- @After
- public void tearDown() {
- if (mInputManagerGlobalSession != null) {
- mInputManagerGlobalSession.close();
- }
+ when(mInputManagerRule.getMock().registerSensorListener(any())).thenReturn(true);
}
private class InputTestSensorEventListener implements SensorEventListener {
@@ -175,13 +163,13 @@ public class InputDeviceSensorManagerTest {
SensorManager sensorManager = device.getSensorManager();
List<Sensor> accelList = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
- verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+ verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID));
assertEquals(1, accelList.size());
assertEquals(DEVICE_ID, accelList.get(0).getId());
assertEquals(Sensor.TYPE_ACCELEROMETER, accelList.get(0).getType());
List<Sensor> gyroList = sensorManager.getSensorList(Sensor.TYPE_GYROSCOPE);
- verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+ verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID));
assertEquals(1, gyroList.size());
assertEquals(DEVICE_ID, gyroList.get(0).getId());
assertEquals(Sensor.TYPE_GYROSCOPE, gyroList.get(0).getType());
@@ -197,11 +185,11 @@ public class InputDeviceSensorManagerTest {
List<Sensor> gameRotationList = sensorManager.getSensorList(
Sensor.TYPE_GAME_ROTATION_VECTOR);
- verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+ verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID));
assertEquals(0, gameRotationList.size());
List<Sensor> gravityList = sensorManager.getSensorList(Sensor.TYPE_GRAVITY);
- verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+ verify(mInputManagerRule.getMock()).getSensorList(eq(DEVICE_ID));
assertEquals(0, gravityList.size());
}
@@ -218,13 +206,13 @@ public class InputDeviceSensorManagerTest {
mIInputSensorEventListener = invocation.getArgument(0);
assertNotNull(mIInputSensorEventListener);
return true;
- }).when(mIInputManagerMock).registerSensorListener(any());
+ }).when(mInputManagerRule.getMock()).registerSensorListener(any());
InputTestSensorEventListener listener = new InputTestSensorEventListener();
assertTrue(sensorManager.registerListener(listener, sensor,
SensorManager.SENSOR_DELAY_NORMAL));
- verify(mIInputManagerMock).registerSensorListener(any());
- verify(mIInputManagerMock).enableSensor(eq(DEVICE_ID), eq(sensor.getType()),
+ verify(mInputManagerRule.getMock()).registerSensorListener(any());
+ verify(mInputManagerRule.getMock()).enableSensor(eq(DEVICE_ID), eq(sensor.getType()),
anyInt(), anyInt());
float[] values = new float[] {0.12f, 9.8f, 0.2f};
@@ -240,7 +228,7 @@ public class InputDeviceSensorManagerTest {
}
sensorManager.unregisterListener(listener);
- verify(mIInputManagerMock).disableSensor(eq(DEVICE_ID), eq(sensor.getType()));
+ verify(mInputManagerRule.getMock()).disableSensor(eq(DEVICE_ID), eq(sensor.getType()));
}
}
diff --git a/tests/Input/src/android/hardware/input/InputManagerTest.kt b/tests/Input/src/android/hardware/input/InputManagerTest.kt
index 152dde94f006..4c6bb849155c 100644
--- a/tests/Input/src/android/hardware/input/InputManagerTest.kt
+++ b/tests/Input/src/android/hardware/input/InputManagerTest.kt
@@ -23,18 +23,16 @@ import android.view.Display
import android.view.DisplayInfo
import android.view.InputDevice
import androidx.test.core.app.ApplicationProvider
-import org.junit.After
-import org.junit.Assert.assertNotNull
+import com.android.test.input.MockInputManagerRule
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.eq
import org.mockito.Mockito.`when`
-import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoJUnitRunner
/**
@@ -54,35 +52,23 @@ class InputManagerTest {
}
@get:Rule
- val rule = MockitoJUnit.rule()!!
+ val inputManagerRule = MockInputManagerRule()
private lateinit var devicesChangedListener: IInputDevicesChangedListener
private val deviceGenerationMap = mutableMapOf<Int /*deviceId*/, Int /*generation*/>()
private lateinit var context: Context
private lateinit var inputManager: InputManager
- @Mock
- private lateinit var iInputManager: IInputManager
- private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
@Before
fun setUp() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
inputManager = InputManager(context)
`when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
- `when`(iInputManager.inputDeviceIds).then {
+ `when`(inputManagerRule.mock.inputDeviceIds).then {
deviceGenerationMap.keys.toIntArray()
}
}
- @After
- fun tearDown() {
- if (this::inputManagerGlobalSession.isInitialized) {
- inputManagerGlobalSession.close()
- }
- }
-
private fun notifyDeviceChanged(
deviceId: Int,
associatedDisplayId: Int,
@@ -92,7 +78,7 @@ class InputManagerTest {
?: throw IllegalArgumentException("Device $deviceId was never added!")
deviceGenerationMap[deviceId] = generation
- `when`(iInputManager.getInputDevice(deviceId))
+ `when`(inputManagerRule.mock.getInputDevice(deviceId))
.thenReturn(createInputDevice(deviceId, associatedDisplayId, usiVersion, generation))
val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) }
if (::devicesChangedListener.isInitialized) {
@@ -125,7 +111,7 @@ class InputManagerTest {
fun testUsiVersionFallBackToDisplayConfig() {
addInputDevice(DEVICE_ID, Display.DEFAULT_DISPLAY, null)
- `when`(iInputManager.getHostUsiVersionFromDisplayConfig(eq(42)))
+ `when`(inputManagerRule.mock.getHostUsiVersionFromDisplayConfig(eq(42)))
.thenReturn(HostUsiVersion(9, 8))
val usiVersion = inputManager.getHostUsiVersion(createDisplay(42))
assertEquals(HostUsiVersion(9, 8), usiVersion)
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
index 072341dcefae..e99c81493394 100644
--- a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
@@ -18,20 +18,17 @@ package android.hardware.input
import android.content.Context
import android.content.ContextWrapper
-import android.os.Handler
import android.os.IBinder
-import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import android.platform.test.flag.junit.SetFlagsRule
import android.view.KeyEvent
import androidx.test.core.app.ApplicationProvider
import com.android.server.testutils.any
-import org.junit.After
+import com.android.test.input.MockInputManagerRule
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.`when`
@@ -69,20 +66,16 @@ class KeyGestureEventHandlerTest {
@get:Rule
val rule = SetFlagsRule()
+ @get:Rule
+ val inputManagerRule = MockInputManagerRule()
- private val testLooper = TestLooper()
private var registeredListener: IKeyGestureHandler? = null
private lateinit var context: Context
private lateinit var inputManager: InputManager
- private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
- @Mock
- private lateinit var iInputManagerMock: IInputManager
@Before
fun setUp() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
inputManager = InputManager(context)
`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
.thenReturn(inputManager)
@@ -97,7 +90,7 @@ class KeyGestureEventHandlerTest {
}
registeredListener = listener
null
- }.`when`(iInputManagerMock).registerKeyGestureHandler(any())
+ }.`when`(inputManagerRule.mock).registerKeyGestureHandler(any())
// Handle key gesture handler being unregistered.
doAnswer {
@@ -108,14 +101,7 @@ class KeyGestureEventHandlerTest {
}
registeredListener = null
null
- }.`when`(iInputManagerMock).unregisterKeyGestureHandler(any())
- }
-
- @After
- fun tearDown() {
- if (this::inputManagerGlobalSession.isInitialized) {
- inputManagerGlobalSession.close()
- }
+ }.`when`(inputManagerRule.mock).unregisterKeyGestureHandler(any())
}
private fun handleKeyGestureEvent(event: KeyGestureEvent) {
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
index ca9de6000a5a..cf0bfcc4f6df 100644
--- a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
@@ -26,20 +26,19 @@ import android.platform.test.flag.junit.SetFlagsRule
import android.view.KeyEvent
import androidx.test.core.app.ApplicationProvider
import com.android.server.testutils.any
-import org.junit.After
+import com.android.test.input.MockInputManagerRule
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.fail
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnitRunner
-import kotlin.test.assertEquals
-import kotlin.test.assertNotNull
-import kotlin.test.assertNull
-import kotlin.test.fail
/**
* Tests for [InputManager.KeyGestureEventListener].
@@ -63,21 +62,18 @@ class KeyGestureEventListenerTest {
@get:Rule
val rule = SetFlagsRule()
+ @get:Rule
+ val inputManagerRule = MockInputManagerRule()
private val testLooper = TestLooper()
private val executor = HandlerExecutor(Handler(testLooper.looper))
private var registeredListener: IKeyGestureEventListener? = null
private lateinit var context: Context
private lateinit var inputManager: InputManager
- private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
- @Mock
- private lateinit var iInputManagerMock: IInputManager
@Before
fun setUp() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
inputManager = InputManager(context)
`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
.thenReturn(inputManager)
@@ -92,7 +88,7 @@ class KeyGestureEventListenerTest {
}
registeredListener = listener
null
- }.`when`(iInputManagerMock).registerKeyGestureEventListener(any())
+ }.`when`(inputManagerRule.mock).registerKeyGestureEventListener(any())
// Handle key gesture event listener being unregistered.
doAnswer {
@@ -103,14 +99,7 @@ class KeyGestureEventListenerTest {
}
registeredListener = null
null
- }.`when`(iInputManagerMock).unregisterKeyGestureEventListener(any())
- }
-
- @After
- fun tearDown() {
- if (this::inputManagerGlobalSession.isInitialized) {
- inputManagerGlobalSession.close()
- }
+ }.`when`(inputManagerRule.mock).unregisterKeyGestureEventListener(any())
}
private fun notifyKeyGestureEvent(event: KeyGestureEvent) {
diff --git a/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
index 23135b2550b0..d25dee1d402c 100644
--- a/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
@@ -24,22 +24,20 @@ import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import androidx.test.core.app.ApplicationProvider
import com.android.server.testutils.any
-import org.junit.After
+import com.android.test.input.MockInputManagerRule
+import java.util.concurrent.Executor
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.fail
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.`when`
-import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoJUnitRunner
-import java.util.concurrent.Executor
-import kotlin.test.assertEquals
-import kotlin.test.assertNotNull
-import kotlin.test.assertNull
-import kotlin.test.fail
/**
* Tests for [InputManager.KeyboardBacklightListener].
@@ -50,23 +48,19 @@ import kotlin.test.fail
@Presubmit
@RunWith(MockitoJUnitRunner::class)
class KeyboardBacklightListenerTest {
+
@get:Rule
- val rule = MockitoJUnit.rule()!!
+ val inputManagerRule = MockInputManagerRule()
private lateinit var testLooper: TestLooper
private var registeredListener: IKeyboardBacklightListener? = null
private lateinit var executor: Executor
private lateinit var context: Context
private lateinit var inputManager: InputManager
- private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
- @Mock
- private lateinit var iInputManagerMock: IInputManager
@Before
fun setUp() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
testLooper = TestLooper()
executor = HandlerExecutor(Handler(testLooper.looper))
registeredListener = null
@@ -84,7 +78,7 @@ class KeyboardBacklightListenerTest {
}
registeredListener = listener
null
- }.`when`(iInputManagerMock).registerKeyboardBacklightListener(any())
+ }.`when`(inputManagerRule.mock).registerKeyboardBacklightListener(any())
// Handle keyboard backlight listener being unregistered.
doAnswer {
@@ -95,14 +89,7 @@ class KeyboardBacklightListenerTest {
}
registeredListener = null
null
- }.`when`(iInputManagerMock).unregisterKeyboardBacklightListener(any())
- }
-
- @After
- fun tearDown() {
- if (this::inputManagerGlobalSession.isInitialized) {
- inputManagerGlobalSession.close()
- }
+ }.`when`(inputManagerRule.mock).unregisterKeyboardBacklightListener(any())
}
private fun notifyKeyboardBacklightChanged(
diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
index bcd56ad0c669..1c2a0538e552 100644
--- a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
@@ -27,21 +27,20 @@ import android.platform.test.flag.junit.SetFlagsRule
import android.view.KeyEvent
import androidx.test.core.app.ApplicationProvider
import com.android.server.testutils.any
-import org.junit.After
+import com.android.test.input.MockInputManagerRule
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.assertTrue
+import kotlin.test.fail
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnitRunner
-import kotlin.test.assertEquals
-import kotlin.test.assertNotNull
-import kotlin.test.assertNull
-import kotlin.test.assertTrue
-import kotlin.test.fail
/**
* Tests for [InputManager.StickyModifierStateListener].
@@ -59,21 +58,18 @@ class StickyModifierStateListenerTest {
@get:Rule
val rule = SetFlagsRule()
+ @get:Rule
+ val inputManagerRule = MockInputManagerRule()
private val testLooper = TestLooper()
private val executor = HandlerExecutor(Handler(testLooper.looper))
private var registeredListener: IStickyModifierStateListener? = null
private lateinit var context: Context
private lateinit var inputManager: InputManager
- private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
-
- @Mock
- private lateinit var iInputManagerMock: IInputManager
@Before
fun setUp() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
inputManager = InputManager(context)
`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
.thenReturn(inputManager)
@@ -88,7 +84,7 @@ class StickyModifierStateListenerTest {
}
registeredListener = listener
null
- }.`when`(iInputManagerMock).registerStickyModifierStateListener(any())
+ }.`when`(inputManagerRule.mock).registerStickyModifierStateListener(any())
// Handle sticky modifier state listener being unregistered.
doAnswer {
@@ -99,14 +95,7 @@ class StickyModifierStateListenerTest {
}
registeredListener = null
null
- }.`when`(iInputManagerMock).unregisterStickyModifierStateListener(any())
- }
-
- @After
- fun tearDown() {
- if (this::inputManagerGlobalSession.isInitialized) {
- inputManagerGlobalSession.close()
- }
+ }.`when`(inputManagerRule.mock).unregisterStickyModifierStateListener(any())
}
private fun notifyStickyModifierStateChanged(modifierState: Int, lockedModifierState: Int) {
diff --git a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
index f2724e605553..044f11d6904c 100644
--- a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
@@ -27,7 +27,6 @@ import android.hardware.input.HostUsiVersion
import android.hardware.input.IInputDeviceBatteryListener
import android.hardware.input.IInputDeviceBatteryState
import android.hardware.input.IInputDevicesChangedListener
-import android.hardware.input.IInputManager
import android.hardware.input.InputManager
import android.hardware.input.InputManagerGlobal
import android.os.Binder
@@ -42,13 +41,13 @@ import com.android.server.input.BatteryController.BluetoothBatteryManager.Blueto
import com.android.server.input.BatteryController.POLLING_PERIOD_MILLIS
import com.android.server.input.BatteryController.UEventBatteryListener
import com.android.server.input.BatteryController.USI_BATTERY_VALIDITY_DURATION_MILLIS
+import com.android.test.input.MockInputManagerRule
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers
import org.hamcrest.TypeSafeMatcher
import org.hamcrest.core.IsEqual.equalTo
-import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -184,12 +183,12 @@ class BatteryControllerTests {
@get:Rule
val rule = MockitoJUnit.rule()!!
+ @get:Rule
+ val inputManagerRule = MockInputManagerRule()
@Mock
private lateinit var native: NativeInputManagerService
@Mock
- private lateinit var iInputManager: IInputManager
- @Mock
private lateinit var uEventManager: UEventManager
@Mock
private lateinit var bluetoothBatteryManager: BluetoothBatteryManager
@@ -205,10 +204,9 @@ class BatteryControllerTests {
fun setup() {
context = TestableContext(ApplicationProvider.getApplicationContext())
testLooper = TestLooper()
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
val inputManager = InputManager(context)
context.addMockSystemService(InputManager::class.java, inputManager)
- `when`(iInputManager.inputDeviceIds).then {
+ `when`(inputManagerRule.mock.inputDeviceIds).then {
deviceGenerationMap.keys.toIntArray()
}
addInputDevice(DEVICE_ID)
@@ -218,18 +216,11 @@ class BatteryControllerTests {
bluetoothBatteryManager)
batteryController.systemRunning()
val listenerCaptor = ArgumentCaptor.forClass(IInputDevicesChangedListener::class.java)
- verify(iInputManager).registerInputDevicesChangedListener(listenerCaptor.capture())
+ verify(inputManagerRule.mock).registerInputDevicesChangedListener(listenerCaptor.capture())
devicesChangedListener = listenerCaptor.value
testLooper.dispatchAll()
}
- @After
- fun tearDown() {
- if (this::inputManagerGlobalSession.isInitialized) {
- inputManagerGlobalSession.close()
- }
- }
-
private fun notifyDeviceChanged(
deviceId: Int,
hasBattery: Boolean = true,
@@ -239,7 +230,7 @@ class BatteryControllerTests {
?: throw IllegalArgumentException("Device $deviceId was never added!")
deviceGenerationMap[deviceId] = generation
- `when`(iInputManager.getInputDevice(deviceId))
+ `when`(inputManagerRule.mock.getInputDevice(deviceId))
.thenReturn(createInputDevice(deviceId, hasBattery, supportsUsi, generation))
val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) }
if (::devicesChangedListener.isInitialized) {
@@ -657,9 +648,9 @@ class BatteryControllerTests {
@Test
fun testRegisterBluetoothListenerForMonitoredBluetoothDevices() {
- `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
.thenReturn("AA:BB:CC:DD:EE:FF")
- `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+ `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
.thenReturn("11:22:33:44:55:66")
addInputDevice(BT_DEVICE_ID)
testLooper.dispatchNext()
@@ -686,7 +677,7 @@ class BatteryControllerTests {
batteryController.unregisterBatteryListener(BT_DEVICE_ID, listener, PID)
verify(bluetoothBatteryManager, never()).removeBatteryListener(any())
- `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+ `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
.thenReturn(null)
notifyDeviceChanged(SECOND_BT_DEVICE_ID)
testLooper.dispatchNext()
@@ -695,7 +686,7 @@ class BatteryControllerTests {
@Test
fun testNotifiesBluetoothBatteryChanges() {
- `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
.thenReturn("AA:BB:CC:DD:EE:FF")
`when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
addInputDevice(BT_DEVICE_ID)
@@ -716,7 +707,7 @@ class BatteryControllerTests {
@Test
fun testBluetoothBatteryIsPrioritized() {
`when`(native.getBatteryDevicePath(BT_DEVICE_ID)).thenReturn("/sys/dev/bt_device")
- `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
.thenReturn("AA:BB:CC:DD:EE:FF")
`when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
`when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(98)
@@ -745,7 +736,7 @@ class BatteryControllerTests {
@Test
fun testFallBackToNativeBatteryStateWhenBluetoothStateInvalid() {
`when`(native.getBatteryDevicePath(BT_DEVICE_ID)).thenReturn("/sys/dev/bt_device")
- `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
.thenReturn("AA:BB:CC:DD:EE:FF")
`when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
`when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(98)
@@ -776,9 +767,9 @@ class BatteryControllerTests {
@Test
fun testRegisterBluetoothMetadataListenerForMonitoredBluetoothDevices() {
- `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
.thenReturn("AA:BB:CC:DD:EE:FF")
- `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+ `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
.thenReturn("11:22:33:44:55:66")
addInputDevice(BT_DEVICE_ID)
testLooper.dispatchNext()
@@ -811,7 +802,7 @@ class BatteryControllerTests {
verify(bluetoothBatteryManager)
.removeMetadataListener("AA:BB:CC:DD:EE:FF", metadataListener1.value)
- `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+ `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
.thenReturn(null)
notifyDeviceChanged(SECOND_BT_DEVICE_ID)
testLooper.dispatchNext()
@@ -821,7 +812,7 @@ class BatteryControllerTests {
@Test
fun testNotifiesBluetoothMetadataBatteryChanges() {
- `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
.thenReturn("AA:BB:CC:DD:EE:FF")
`when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF",
BluetoothDevice.METADATA_MAIN_BATTERY))
@@ -861,7 +852,7 @@ class BatteryControllerTests {
@Test
fun testBluetoothMetadataBatteryIsPrioritized() {
- `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ `when`(inputManagerRule.mock.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
.thenReturn("AA:BB:CC:DD:EE:FF")
`when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
`when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF",
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 351ec4635977..927958eb62cc 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -93,14 +93,12 @@ class InputManagerServiceTests {
)
}
- @JvmField
- @Rule
+ @get:Rule
val extendedMockitoRule =
ExtendedMockitoRule.Builder(this).mockStatic(LocalServices::class.java)
.mockStatic(PermissionChecker::class.java).build()!!
- @JvmField
- @Rule
+ @get:Rule
val setFlagsRule = SetFlagsRule()
@get:Rule
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 4ae06a4f9812..7526737f60bf 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -46,6 +46,7 @@ import com.android.internal.util.FrameworkStatsLog
import com.android.modules.utils.testing.ExtendedMockitoRule
import junitparams.JUnitParamsRunner
import junitparams.Parameters
+import org.junit.After
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
@@ -128,6 +129,13 @@ class KeyGestureControllerTests {
currentPid = Process.myPid()
}
+ @After
+ fun teardown() {
+ if (this::inputManagerGlobalSession.isInitialized) {
+ inputManagerGlobalSession.close()
+ }
+ }
+
private fun setupBehaviors() {
Mockito.`when`(
resources.getBoolean(
diff --git a/tests/Input/src/com/android/server/input/KeyRemapperTests.kt b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
index f74fd723d540..4f4c97bef4c0 100644
--- a/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
@@ -18,16 +18,18 @@ package com.android.server.input
import android.content.Context
import android.content.ContextWrapper
-import android.hardware.input.IInputManager
import android.hardware.input.InputManager
-import android.hardware.input.InputManagerGlobal
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import android.provider.Settings
import android.view.InputDevice
import android.view.KeyEvent
import androidx.test.core.app.ApplicationProvider
-import org.junit.After
+import com.android.test.input.MockInputManagerRule
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
@@ -35,10 +37,6 @@ import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.junit.MockitoJUnit
-import java.io.FileNotFoundException
-import java.io.FileOutputStream
-import java.io.IOException
-import java.io.InputStream
private fun createKeyboard(deviceId: Int): InputDevice =
InputDevice.Builder()
@@ -73,15 +71,15 @@ class KeyRemapperTests {
@get:Rule
val rule = MockitoJUnit.rule()!!
- @Mock
- private lateinit var iInputManager: IInputManager
+ @get:Rule
+ val inputManagerRule = MockInputManagerRule()
+
@Mock
private lateinit var native: NativeInputManagerService
private lateinit var mKeyRemapper: KeyRemapper
private lateinit var context: Context
private lateinit var dataStore: PersistentDataStore
private lateinit var testLooper: TestLooper
- private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
@Before
fun setup() {
@@ -104,25 +102,17 @@ class KeyRemapperTests {
dataStore,
testLooper.looper
)
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
val inputManager = InputManager(context)
Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
.thenReturn(inputManager)
- Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
- }
-
- @After
- fun tearDown() {
- if (this::inputManagerGlobalSession.isInitialized) {
- inputManagerGlobalSession.close()
- }
+ Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
}
@Test
fun testKeyRemapping_whenRemappingEnabled() {
ModifierRemappingFlag(true).use {
val keyboard = createKeyboard(DEVICE_ID)
- Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
+ Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
for (i in REMAPPABLE_KEYS.indices) {
val fromKeyCode = REMAPPABLE_KEYS[i]
@@ -160,7 +150,7 @@ class KeyRemapperTests {
fun testKeyRemapping_whenRemappingDisabled() {
ModifierRemappingFlag(false).use {
val keyboard = createKeyboard(DEVICE_ID)
- Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
+ Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
mKeyRemapper.remapKey(REMAPPABLE_KEYS[0], REMAPPABLE_KEYS[1])
testLooper.dispatchAll()
diff --git a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
index 59aa96c46336..58fb4e1ed103 100644
--- a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
@@ -20,11 +20,9 @@ import android.animation.ValueAnimator
import android.content.Context
import android.content.ContextWrapper
import android.graphics.Color
-import android.hardware.input.IInputManager
import android.hardware.input.IKeyboardBacklightListener
import android.hardware.input.IKeyboardBacklightState
import android.hardware.input.InputManager
-import android.hardware.input.InputManagerGlobal
import android.hardware.lights.Light
import android.os.UEventObserver
import android.os.test.TestLooper
@@ -35,7 +33,11 @@ import androidx.test.core.app.ApplicationProvider
import com.android.server.input.KeyboardBacklightController.DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL
import com.android.server.input.KeyboardBacklightController.MAX_BRIGHTNESS_CHANGE_STEPS
import com.android.server.input.KeyboardBacklightController.USER_INACTIVITY_THRESHOLD_MILLIS
-import org.junit.After
+import com.android.test.input.MockInputManagerRule
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
@@ -52,10 +54,6 @@ import org.mockito.Mockito.eq
import org.mockito.Mockito.spy
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
-import java.io.FileNotFoundException
-import java.io.FileOutputStream
-import java.io.IOException
-import java.io.InputStream
private fun createKeyboard(deviceId: Int): InputDevice =
InputDevice.Builder()
@@ -100,10 +98,10 @@ class KeyboardBacklightControllerTests {
@get:Rule
val rule = MockitoJUnit.rule()!!
+ @get:Rule
+ val inputManagerRule = MockInputManagerRule()
@Mock
- private lateinit var iInputManager: IInputManager
- @Mock
private lateinit var native: NativeInputManagerService
@Mock
private lateinit var uEventManager: UEventManager
@@ -111,7 +109,6 @@ class KeyboardBacklightControllerTests {
private lateinit var context: Context
private lateinit var dataStore: PersistentDataStore
private lateinit var testLooper: TestLooper
- private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
private var lightColorMap: HashMap<Int, Int> = HashMap()
private var lastBacklightState: KeyboardBacklightState? = null
private var sysfsNodeChanges = 0
@@ -134,10 +131,9 @@ class KeyboardBacklightControllerTests {
testLooper = TestLooper()
keyboardBacklightController = KeyboardBacklightController(context, native, dataStore,
testLooper.looper, FakeAnimatorFactory(), uEventManager)
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
val inputManager = InputManager(context)
`when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
- `when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
+ `when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
`when`(native.setLightColor(anyInt(), anyInt(), anyInt())).then {
val args = it.arguments
lightColorMap.put(args[1] as Int, args[2] as Int)
@@ -152,13 +148,6 @@ class KeyboardBacklightControllerTests {
}
}
- @After
- fun tearDown() {
- if (this::inputManagerGlobalSession.isInitialized) {
- inputManagerGlobalSession.close()
- }
- }
-
@Test
fun testKeyboardBacklightIncrementDecrement() {
KeyboardBacklightFlags(
@@ -168,8 +157,9 @@ class KeyboardBacklightControllerTests {
).use {
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
@@ -186,8 +176,9 @@ class KeyboardBacklightControllerTests {
).use {
val keyboardWithoutBacklight = createKeyboard(DEVICE_ID)
val keyboardInputLight = createLight(LIGHT_ID, Light.LIGHT_TYPE_INPUT)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithoutBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithoutBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
incrementKeyboardBacklight(DEVICE_ID)
@@ -205,8 +196,9 @@ class KeyboardBacklightControllerTests {
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
val keyboardInputLight = createLight(SECOND_LIGHT_ID, Light.LIGHT_TYPE_INPUT)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(
listOf(
keyboardBacklight,
keyboardInputLight
@@ -230,8 +222,9 @@ class KeyboardBacklightControllerTests {
).use {
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
for (level in 1 until DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size) {
dataStore.setKeyboardBacklightBrightness(
@@ -263,7 +256,8 @@ class KeyboardBacklightControllerTests {
).use {
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
dataStore.setKeyboardBacklightBrightness(
keyboardWithBacklight.descriptor,
LIGHT_ID,
@@ -278,7 +272,7 @@ class KeyboardBacklightControllerTests {
lightColorMap.isEmpty()
)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceChanged(DEVICE_ID)
keyboardBacklightController.notifyUserActivity()
testLooper.dispatchNext()
@@ -300,8 +294,9 @@ class KeyboardBacklightControllerTests {
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
val maxLevel = DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size - 1
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
// Register backlight listener
@@ -352,8 +347,9 @@ class KeyboardBacklightControllerTests {
).use {
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
dataStore.setKeyboardBacklightBrightness(
keyboardWithBacklight.descriptor,
LIGHT_ID,
@@ -388,8 +384,9 @@ class KeyboardBacklightControllerTests {
).use {
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
dataStore.setKeyboardBacklightBrightness(
keyboardWithBacklight.descriptor,
LIGHT_ID,
@@ -482,8 +479,9 @@ class KeyboardBacklightControllerTests {
).use {
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
incrementKeyboardBacklight(DEVICE_ID)
@@ -511,8 +509,9 @@ class KeyboardBacklightControllerTests {
val suggestedLevels = intArrayOf(0, 22, 63, 135, 196, 255)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
suggestedLevels)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
@@ -531,8 +530,9 @@ class KeyboardBacklightControllerTests {
val suggestedLevels = IntArray(MAX_BRIGHTNESS_CHANGE_STEPS + 1) { 10 * (it + 1) }
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
suggestedLevels)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
@@ -551,8 +551,9 @@ class KeyboardBacklightControllerTests {
val suggestedLevels = intArrayOf(22, 63, 135, 196)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
suggestedLevels)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
// Framework will add the lowest and maximum levels if not provided via config
@@ -572,8 +573,10 @@ class KeyboardBacklightControllerTests {
val suggestedLevels = intArrayOf(22, 63, 135, 400, 196, 1000)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
suggestedLevels)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID))
+ .thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
// Framework will drop out of bound levels in the config
@@ -591,8 +594,9 @@ class KeyboardBacklightControllerTests {
).use {
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
dataStore.setKeyboardBacklightBrightness(
keyboardWithBacklight.descriptor,
@@ -619,8 +623,9 @@ class KeyboardBacklightControllerTests {
).use {
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
incrementKeyboardBacklight(DEVICE_ID)
@@ -642,8 +647,9 @@ class KeyboardBacklightControllerTests {
).use {
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
sendAmbientBacklightValue(1)
assertEquals(
@@ -671,8 +677,9 @@ class KeyboardBacklightControllerTests {
).use {
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
sendAmbientBacklightValue(254)
assertEquals(
@@ -701,8 +708,9 @@ class KeyboardBacklightControllerTests {
).use {
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
- `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
- `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ `when`(inputManagerRule.mock.getInputDevice(DEVICE_ID))
+ .thenReturn(keyboardWithBacklight)
+ `when`(inputManagerRule.mock.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
incrementKeyboardBacklight(DEVICE_ID)
assertEquals(
diff --git a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
index c073c7aae678..ff8a9ba94353 100644
--- a/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardGlyphManagerTests.kt
@@ -23,9 +23,7 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.content.pm.ServiceInfo
-import android.hardware.input.IInputManager
import android.hardware.input.InputManager
-import android.hardware.input.InputManagerGlobal
import android.hardware.input.KeyGlyphMap.KeyCombination
import android.os.Bundle
import android.os.test.TestLooper
@@ -36,8 +34,8 @@ import android.view.InputDevice
import android.view.KeyEvent
import androidx.test.core.app.ApplicationProvider
import com.android.hardware.input.Flags
+import com.android.test.input.MockInputManagerRule
import com.android.test.input.R
-import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
@@ -65,30 +63,24 @@ class KeyboardGlyphManagerTests {
const val RECEIVER_NAME = "DummyReceiver"
}
- @JvmField
- @Rule(order = 0)
+ @get:Rule
val setFlagsRule = SetFlagsRule()
-
- @JvmField
- @Rule(order = 1)
+ @get:Rule
val mockitoRule = MockitoJUnit.rule()!!
+ @get:Rule
+ val inputManagerRule = MockInputManagerRule()
@Mock
private lateinit var packageManager: PackageManager
- @Mock
- private lateinit var iInputManager: IInputManager
-
private lateinit var keyboardGlyphManager: KeyboardGlyphManager
private lateinit var context: Context
private lateinit var testLooper: TestLooper
- private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
private lateinit var keyboardDevice: InputDevice
@Before
fun setup() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
testLooper = TestLooper()
keyboardGlyphManager = KeyboardGlyphManager(context, testLooper.looper)
@@ -98,21 +90,14 @@ class KeyboardGlyphManagerTests {
testLooper.dispatchAll()
}
- @After
- fun tearDown() {
- if (this::inputManagerGlobalSession.isInitialized) {
- inputManagerGlobalSession.close()
- }
- }
-
private fun setupInputDevices() {
val inputManager = InputManager(context)
Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
.thenReturn(inputManager)
keyboardDevice = createKeyboard(DEVICE_ID, VENDOR_ID, PRODUCT_ID, 0, "", "")
- Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
- Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
+ Mockito.`when`(inputManagerRule.mock.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
+ Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
}
private fun setupBroadcastReceiver() {
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index 301c0e6a159f..d6654cceb458 100644
--- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -24,11 +24,10 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.content.pm.ServiceInfo
-import android.hardware.input.KeyboardLayoutSelectionResult
-import android.hardware.input.IInputManager
import android.hardware.input.InputManager
import android.hardware.input.InputManagerGlobal
import android.hardware.input.KeyboardLayout
+import android.hardware.input.KeyboardLayoutSelectionResult
import android.icu.util.ULocale
import android.os.Bundle
import android.os.test.TestLooper
@@ -42,8 +41,12 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.internal.os.KeyboardConfiguredProto
import com.android.internal.util.FrameworkStatsLog
import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.test.input.MockInputManagerRule
import com.android.test.input.R
-import org.junit.After
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue
@@ -53,10 +56,6 @@ import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.Mock
import org.mockito.Mockito
-import java.io.FileNotFoundException
-import java.io.FileOutputStream
-import java.io.IOException
-import java.io.InputStream
fun createKeyboard(
deviceId: Int,
@@ -120,8 +119,8 @@ class KeyboardLayoutManagerTests {
val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
.mockStatic(FrameworkStatsLog::class.java).build()!!
- @Mock
- private lateinit var iInputManager: IInputManager
+ @get:Rule
+ val inputManagerRule = MockInputManagerRule()
@Mock
private lateinit var native: NativeInputManagerService
@@ -148,7 +147,6 @@ class KeyboardLayoutManagerTests {
@Before
fun setup() {
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
dataStore = PersistentDataStore(object : PersistentDataStore.Injector() {
override fun openRead(): InputStream? {
throw FileNotFoundException()
@@ -171,13 +169,6 @@ class KeyboardLayoutManagerTests {
setupIme()
}
- @After
- fun tearDown() {
- if (this::inputManagerGlobalSession.isInitialized) {
- inputManagerGlobalSession.close()
- }
- }
-
private fun setupInputDevices() {
val inputManager = InputManager(context)
Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
@@ -191,19 +182,19 @@ class KeyboardLayoutManagerTests {
DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "dvorak")
englishQwertyKeyboardDevice = createKeyboard(ENGLISH_QWERTY_DEVICE_ID, DEFAULT_VENDOR_ID,
DEFAULT_PRODUCT_ID, DEFAULT_DEVICE_BUS, "en", "qwerty")
- Mockito.`when`(iInputManager.inputDeviceIds)
+ Mockito.`when`(inputManagerRule.mock.inputDeviceIds)
.thenReturn(intArrayOf(
DEVICE_ID,
VENDOR_SPECIFIC_DEVICE_ID,
ENGLISH_DVORAK_DEVICE_ID,
ENGLISH_QWERTY_DEVICE_ID
))
- Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
- Mockito.`when`(iInputManager.getInputDevice(VENDOR_SPECIFIC_DEVICE_ID))
+ Mockito.`when`(inputManagerRule.mock.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
+ Mockito.`when`(inputManagerRule.mock.getInputDevice(VENDOR_SPECIFIC_DEVICE_ID))
.thenReturn(vendorSpecificKeyboardDevice)
- Mockito.`when`(iInputManager.getInputDevice(ENGLISH_DVORAK_DEVICE_ID))
+ Mockito.`when`(inputManagerRule.mock.getInputDevice(ENGLISH_DVORAK_DEVICE_ID))
.thenReturn(englishDvorakKeyboardDevice)
- Mockito.`when`(iInputManager.getInputDevice(ENGLISH_QWERTY_DEVICE_ID))
+ Mockito.`when`(inputManagerRule.mock.getInputDevice(ENGLISH_QWERTY_DEVICE_ID))
.thenReturn(englishQwertyKeyboardDevice)
}
diff --git a/tests/Input/src/com/android/test/input/MockInputManagerRule.kt b/tests/Input/src/com/android/test/input/MockInputManagerRule.kt
new file mode 100644
index 000000000000..cef985600c40
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/MockInputManagerRule.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 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.test.input
+
+import android.hardware.input.IInputManager
+import android.hardware.input.InputManagerGlobal
+import org.junit.rules.ExternalResource
+import org.mockito.Mockito
+
+/**
+ * A test rule that temporarily replaces the [IInputManager] connection to the server with a mock
+ * to be used for testing.
+ */
+class MockInputManagerRule : ExternalResource() {
+
+ private lateinit var session: InputManagerGlobal.TestSession
+
+ val mock: IInputManager = Mockito.mock(IInputManager::class.java)
+
+ override fun before() {
+ session = InputManagerGlobal.createTestSession(mock)
+ }
+
+ override fun after() {
+ if (this::session.isInitialized) {
+ session.close()
+ }
+ }
+}
diff --git a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt
index 9f4df90422eb..c61a25021949 100644
--- a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt
+++ b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt
@@ -39,7 +39,6 @@ import com.android.cts.input.inputeventmatchers.withSource
import junit.framework.Assert.fail
import org.hamcrest.Matchers.allOf
import org.junit.Before
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestName
@@ -108,7 +107,6 @@ class UinputRecordingIntegrationTests {
parser = InputJsonParser(instrumentation.context)
}
- @Ignore("b/366602644")
@Test
fun testEvemuRecording() {
VirtualDisplayActivityScenario.AutoClose<CaptureEventActivity>(
diff --git a/tests/testables/src/android/testing/TestWithLooperRule.java b/tests/testables/src/android/testing/TestWithLooperRule.java
index 10df17f991d3..6a8e142e2314 100644
--- a/tests/testables/src/android/testing/TestWithLooperRule.java
+++ b/tests/testables/src/android/testing/TestWithLooperRule.java
@@ -100,6 +100,9 @@ public class TestWithLooperRule implements MethodRule {
case "ExpectException":
next = this.getNextStatement(next, "next");
break;
+ case "UiThreadStatement":
+ next = this.getNextStatement(next, "base");
+ break;
default:
throw new Exception(
String.format("Unexpected Statement received: [%s]",
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 37b1687fd3f1..5983cf165839 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -28,8 +28,19 @@ using namespace std::literals;
namespace aapt {
static constexpr ApiVersion sDevelopmentSdkLevel = 10000;
+
+// clang-format off
static constexpr StringPiece sDevelopmentSdkCodeNames[] = {
- "Q"sv, "R"sv, "S"sv, "Sv2"sv, "Tiramisu"sv, "UpsideDownCake"sv, "VanillaIceCream"sv};
+ "Q"sv,
+ "R"sv,
+ "S"sv,
+ "Sv2"sv,
+ "Tiramisu"sv,
+ "UpsideDownCake"sv,
+ "VanillaIceCream"sv,
+ "Baklava"sv,
+};
+// clang-format on
static constexpr auto sPrivacySandboxSuffix = "PrivacySandbox"sv;
diff --git a/tools/hoststubgen/.gitignore b/tools/hoststubgen/.gitignore
deleted file mode 100644
index 6453bdef8cee..000000000000
--- a/tools/hoststubgen/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-out/
-*-out/
-*.log
diff --git a/tools/hoststubgen/OWNERS b/tools/hoststubgen/OWNERS
deleted file mode 100644
index 3d8888d83cf4..000000000000
--- a/tools/hoststubgen/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file:platform/frameworks/base:/ravenwood/OWNERS
diff --git a/tools/hoststubgen/TEST_MAPPING b/tools/hoststubgen/TEST_MAPPING
deleted file mode 100644
index 856e6eefba15..000000000000
--- a/tools/hoststubgen/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "imports": [
- {
- "path": "frameworks/base/ravenwood"
- }
- ]
-}
diff --git a/tools/hoststubgen/hoststubgen/.gitignore b/tools/hoststubgen/hoststubgen/.gitignore
deleted file mode 100644
index 0f384074ed7f..000000000000
--- a/tools/hoststubgen/hoststubgen/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-framework-all-stub-out \ No newline at end of file
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
index cba521e639cb..196b5e7c02ab 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -22,8 +22,6 @@ import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.MethodSpec
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeSpec
-import java.util.HashMap
-import java.util.Map
import javax.lang.model.element.Modifier
/*
@@ -52,7 +50,7 @@ import javax.lang.model.element.Modifier
* public static boolean hasFeatureAutomotive(Context context);
* public static boolean hasFeatureLeanback(Context context);
* public static Boolean maybeHasFeature(String feature, int version);
- * public static ArrayMap<String, FeatureInfo> getCompileTimeAvailableFeatures();
+ * public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures();
* }
* </pre>
*/
@@ -63,6 +61,7 @@ object SystemFeaturesGenerator {
private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager")
private val CONTEXT_CLASS = ClassName.get("android.content", "Context")
private val FEATUREINFO_CLASS = ClassName.get("android.content.pm", "FeatureInfo")
+ private val ARRAYMAP_CLASS = ClassName.get("android.util", "ArrayMap")
private val ASSUME_TRUE_CLASS =
ClassName.get("com.android.aconfig.annotations", "AssumeTrueForR8")
private val ASSUME_FALSE_CLASS =
@@ -291,19 +290,19 @@ object SystemFeaturesGenerator {
features: Collection<FeatureInfo>,
) {
val methodBuilder =
- MethodSpec.methodBuilder("getCompileTimeAvailableFeatures")
+ MethodSpec.methodBuilder("getReadOnlySystemEnabledFeatures")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addAnnotation(ClassName.get("android.annotation", "NonNull"))
.addJavadoc("Gets features marked as available at compile-time, keyed by name." +
"\n\n@hide")
.returns(ParameterizedTypeName.get(
- ClassName.get(Map::class.java),
+ ARRAYMAP_CLASS,
ClassName.get(String::class.java),
FEATUREINFO_CLASS))
val availableFeatures = features.filter { it.readonly && it.version != null }
- methodBuilder.addStatement("Map<String, FeatureInfo> features = new \$T<>(\$L)",
- HashMap::class.java, availableFeatures.size)
+ methodBuilder.addStatement("\$T<String, FeatureInfo> features = new \$T<>(\$L)",
+ ARRAYMAP_CLASS, ARRAYMAP_CLASS, availableFeatures.size)
if (!availableFeatures.isEmpty()) {
methodBuilder.addStatement("FeatureInfo fi = new FeatureInfo()")
}
diff --git a/tools/systemfeatures/tests/golden/RoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
index edbfc4237547..ee97b26159de 100644
--- a/tools/systemfeatures/tests/golden/RoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
@@ -13,10 +13,9 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
+import android.util.ArrayMap;
import com.android.aconfig.annotations.AssumeFalseForR8;
import com.android.aconfig.annotations.AssumeTrueForR8;
-import java.util.HashMap;
-import java.util.Map;
/**
* @hide
@@ -94,8 +93,8 @@ public final class RoFeatures {
* @hide
*/
@NonNull
- public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
- Map<String, FeatureInfo> features = new HashMap<>(2);
+ public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+ ArrayMap<String, FeatureInfo> features = new ArrayMap<>(2);
FeatureInfo fi = new FeatureInfo();
fi.name = PackageManager.FEATURE_WATCH;
fi.version = 1;
diff --git a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
index bf7a00679fa6..40c7db7ff1df 100644
--- a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
@@ -9,8 +9,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
-import java.util.HashMap;
-import java.util.Map;
+import android.util.ArrayMap;
/**
* @hide
@@ -43,8 +42,8 @@ public final class RoNoFeatures {
* @hide
*/
@NonNull
- public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
- Map<String, FeatureInfo> features = new HashMap<>(0);
+ public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+ ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0);
return features;
}
}
diff --git a/tools/systemfeatures/tests/golden/RwFeatures.java.gen b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
index b20b228f9814..7bf89614b92d 100644
--- a/tools/systemfeatures/tests/golden/RwFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
@@ -12,8 +12,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
-import java.util.HashMap;
-import java.util.Map;
+import android.util.ArrayMap;
/**
* @hide
@@ -73,8 +72,8 @@ public final class RwFeatures {
* @hide
*/
@NonNull
- public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
- Map<String, FeatureInfo> features = new HashMap<>(0);
+ public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+ ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0);
return features;
}
}
diff --git a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
index d91f5b62d8d4..eb7ec63f1d7d 100644
--- a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
@@ -7,8 +7,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.FeatureInfo;
-import java.util.HashMap;
-import java.util.Map;
+import android.util.ArrayMap;
/**
* @hide
@@ -32,8 +31,8 @@ public final class RwNoFeatures {
* @hide
*/
@NonNull
- public static Map<String, FeatureInfo> getCompileTimeAvailableFeatures() {
- Map<String, FeatureInfo> features = new HashMap<>(0);
+ public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures() {
+ ArrayMap<String, FeatureInfo> features = new ArrayMap<>(0);
return features;
}
}
diff --git a/tools/systemfeatures/tests/src/ArrayMap.java b/tools/systemfeatures/tests/src/ArrayMap.java
new file mode 100644
index 000000000000..a5ed9b088896
--- /dev/null
+++ b/tools/systemfeatures/tests/src/ArrayMap.java
@@ -0,0 +1,26 @@
+/*
+ * 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 android.util;
+
+import java.util.HashMap;
+
+/** Stub for testing. */
+public final class ArrayMap<K, V> extends HashMap<K, V> {
+ public ArrayMap(int capacity) {
+ super(capacity);
+ }
+}
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
index 39f8fc44fe23..ed3f5c94ba79 100644
--- a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
+++ b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
@@ -60,7 +60,7 @@ public class SystemFeaturesGeneratorTest {
assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
assertThat(RwNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
- assertThat(RwNoFeatures.getCompileTimeAvailableFeatures()).isEmpty();
+ assertThat(RwNoFeatures.getReadOnlySystemEnabledFeatures()).isEmpty();
}
@Test
@@ -72,7 +72,7 @@ public class SystemFeaturesGeneratorTest {
assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
assertThat(RoNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
- assertThat(RoNoFeatures.getCompileTimeAvailableFeatures()).isEmpty();
+ assertThat(RoNoFeatures.getReadOnlySystemEnabledFeatures()).isEmpty();
// Also ensure we fall back to the PackageManager for feature APIs without an accompanying
// versioned feature definition.
@@ -106,7 +106,7 @@ public class SystemFeaturesGeneratorTest {
assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
assertThat(RwFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
- assertThat(RwFeatures.getCompileTimeAvailableFeatures()).isEmpty();
+ assertThat(RwFeatures.getReadOnlySystemEnabledFeatures()).isEmpty();
}
@Test
@@ -163,7 +163,7 @@ public class SystemFeaturesGeneratorTest {
assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", 100)).isNull();
assertThat(RoFeatures.maybeHasFeature("", 0)).isNull();
- Map<String, FeatureInfo> compiledFeatures = RoFeatures.getCompileTimeAvailableFeatures();
+ Map<String, FeatureInfo> compiledFeatures = RoFeatures.getReadOnlySystemEnabledFeatures();
assertThat(compiledFeatures.keySet())
.containsExactly(PackageManager.FEATURE_WATCH, PackageManager.FEATURE_WIFI);
assertThat(compiledFeatures.get(PackageManager.FEATURE_WATCH).version).isEqualTo(1);