summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java14
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java3
-rw-r--r--core/api/test-current.txt8
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java4
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java2
-rw-r--r--core/java/android/app/AppOpsManager.java2
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl3
-rw-r--r--core/java/android/app/LoadedApk.java10
-rw-r--r--core/java/android/app/NotificationChannel.java86
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java6
-rw-r--r--core/java/android/content/ContentProvider.java15
-rw-r--r--core/java/android/content/ContentResolver.java18
-rw-r--r--core/java/android/content/Context.java2
-rw-r--r--core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java6
-rw-r--r--core/java/android/hardware/face/FaceManager.java13
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java9
-rw-r--r--core/java/android/hardware/usb/UsbPortStatus.java8
-rw-r--r--core/java/android/os/GraphicsEnvironment.java8
-rw-r--r--core/java/android/provider/Settings.java31
-rw-r--r--core/java/android/service/dreams/DreamManagerInternal.java15
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java7
-rw-r--r--core/java/android/view/Display.java10
-rw-r--r--core/java/android/view/DisplayInfo.java17
-rw-r--r--core/java/android/view/HandwritingInitiator.java7
-rw-r--r--core/java/android/view/SurfaceControl.java20
-rw-r--r--core/java/android/view/ViewRootImpl.java66
-rw-r--r--core/java/android/view/WindowManager.java2
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java1
-rw-r--r--core/java/android/widget/TextView.java24
-rw-r--r--core/java/android/window/SurfaceSyncGroup.java37
-rw-r--r--core/java/android/window/TaskConstants.java21
-rw-r--r--core/java/android/window/TransitionInfo.java35
-rw-r--r--core/java/com/android/internal/app/AssistUtils.java2
-rw-r--r--core/java/com/android/internal/display/RefreshRateSettingsUtils.java92
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java33
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java80
-rw-r--r--core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java240
-rw-r--r--core/jni/android_graphics_BLASTBufferQueue.cpp14
-rw-r--r--core/jni/android_os_GraphicsEnvironment.cpp6
-rw-r--r--core/jni/android_view_SurfaceControl.cpp11
-rw-r--r--core/jni/core_jni_helpers.h9
-rw-r--r--core/proto/android/server/windowmanagertransitiontrace.proto2
-rw-r--r--core/res/AndroidManifest.xml17
-rw-r--r--core/res/res/drawable/loading_spinner.xml55
-rw-r--r--core/res/res/layout/user_switching_dialog.xml53
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/tests/coretests/Android.bp2
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java4
-rw-r--r--core/tests/coretests/src/android/hardware/face/FaceManagerTest.java13
-rw-r--r--core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java17
-rw-r--r--core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java114
-rw-r--r--core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java96
-rw-r--r--core/tests/mockingcoretests/src/android/view/DisplayTest.java30
-rw-r--r--data/etc/services.core.protolog.json6
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java41
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java12
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java9
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentation.java4
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java9
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java44
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt52
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java109
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java70
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java93
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java320
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java49
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java182
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java245
-rw-r--r--libs/hwui/DamageAccumulator.cpp43
-rw-r--r--libs/hwui/DamageAccumulator.h4
-rw-r--r--libs/hwui/jni/BitmapFactory.cpp8
-rw-r--r--libs/hwui/jni/BitmapFactory.h6
-rw-r--r--libs/hwui/jni/ImageDecoder.cpp14
-rw-r--r--libs/hwui/jni/android_graphics_RenderNode.cpp74
-rw-r--r--media/java/android/media/ImageUtils.java18
-rw-r--r--media/java/android/media/VolumeProvider.java41
-rw-r--r--media/java/android/media/session/MediaController.java59
-rw-r--r--media/java/android/media/soundtrigger/SoundTriggerManager.java103
-rw-r--r--packages/CompanionDeviceManager/res/layout/activity_confirmation.xml9
-rw-r--r--packages/CompanionDeviceManager/res/layout/list_item_permission.xml3
-rw-r--r--packages/CompanionDeviceManager/res/layout/vendor_header.xml2
-rw-r--r--packages/CompanionDeviceManager/res/values/strings.xml17
-rw-r--r--packages/CompanionDeviceManager/res/values/styles.xml2
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java25
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java4
-rw-r--r--[-rwxr-xr-x]packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java8
-rw-r--r--packages/SettingsLib/res/drawable/dialog_btn_filled.xml39
-rw-r--r--packages/SettingsLib/res/drawable/dialog_btn_outline.xml42
-rw-r--r--packages/SettingsLib/res/drawable/ic_admin_panel_settings.xml25
-rw-r--r--packages/SettingsLib/res/layout/dialog_with_icon.xml99
-rw-r--r--packages/SettingsLib/res/values/dimens.xml9
-rw-r--r--packages/SettingsLib/res/values/styles.xml35
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java277
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java77
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java54
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java3
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java32
-rw-r--r--packages/Shell/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/Android.bp5
-rw-r--r--packages/SystemUI/AndroidManifest.xml5
-rw-r--r--packages/SystemUI/animation/Android.bp1
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt2
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt2
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt16
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java212
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/InterpolatorsAndroidX.java219
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt2
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt49
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt6
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt1
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt2
-rw-r--r--packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt3
-rw-r--r--packages/SystemUI/checks/src/com/android/internal/systemui/lint/TestFunctionNameViolationDetector.kt89
-rw-r--r--packages/SystemUI/checks/tests/com/android/internal/systemui/lint/TestFunctionNameViolationDetectorTest.kt103
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt4
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt7
-rw-r--r--packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java6
-rw-r--r--packages/SystemUI/res/layout/status_bar.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml29
-rw-r--r--packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationDialogFactory.java177
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java206
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt2
-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/FromDozingTransitionInteractor.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/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/FromOccludedTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.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/viewmodel/DreamingToLockscreenTransitionViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskLabelLoader.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt113
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualLocationsService.kt93
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/InterpolatorsAndroidXTest.kt55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java174
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java152
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt108
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/PropertyAnimatorTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt64
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt128
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java23
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java65
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java104
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java6
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationFeatureFlagBase.java115
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java92
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java46
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java2
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java11
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/FillUi.java26
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java4
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java332
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java188
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java44
-rw-r--r--services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java62
-rw-r--r--services/companion/java/com/android/server/companion/virtual/InputController.java60
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java59
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java15
-rw-r--r--services/core/Android.bp2
-rw-r--r--services/core/java/android/os/BatteryStatsInternal.java24
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java5
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java43
-rw-r--r--services/core/java/com/android/server/am/UserController.java97
-rw-r--r--services/core/java/com/android/server/am/UserSwitchingDialog.java304
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java13
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java49
-rw-r--r--services/core/java/com/android/server/audio/AudioServiceEvents.java37
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java11
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java3
-rw-r--r--services/core/java/com/android/server/biometrics/OWNERS4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java8
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java32
-rw-r--r--services/core/java/com/android/server/display/BrightnessMappingStrategy.java48
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java10
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java38
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java33
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java28
-rw-r--r--services/core/java/com/android/server/display/HighBrightnessModeController.java127
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java34
-rw-r--r--services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java24
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java102
-rw-r--r--services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java4
-rw-r--r--services/core/java/com/android/server/dreams/DreamController.java2
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java28
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java111
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java10
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java20
-rw-r--r--services/core/java/com/android/server/pm/BroadcastHelper.java2
-rw-r--r--services/core/java/com/android/server/pm/DexOptHelper.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java3
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java34
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java20
-rw-r--r--services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java17
-rw-r--r--services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java2
-rw-r--r--services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java17
-rw-r--r--services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java250
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java62
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityInterceptorCallback.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java18
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java35
-rw-r--r--services/core/java/com/android/server/wm/BLASTSyncEngine.java30
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java3
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java31
-rw-r--r--services/core/java/com/android/server/wm/Transition.java7
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java115
-rw-r--r--services/core/java/com/android/server/wm/TransitionTracer.java3
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java31
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java31
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java2
-rw-r--r--services/core/jni/OWNERS1
-rw-r--r--services/core/jni/com_android_server_companion_virtual_InputController.cpp42
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp40
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd6
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt2
-rw-r--r--services/credentials/java/com/android/server/credentials/GetRequestSession.java8
-rw-r--r--services/credentials/java/com/android/server/credentials/MetricUtilities.java55
-rw-r--r--services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java3
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderClearSession.java2
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderCreateSession.java2
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderGetSession.java2
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/ApiName.java4
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java4
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java42
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java24
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/EntryEnum.java4
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java53
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java12
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/ProviderStatusForMetrics.java4
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java34
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java251
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java1
-rw-r--r--services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java244
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java74
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java88
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java121
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/OWNERS3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java2
-rw-r--r--services/tests/servicestests/res/xml/irq_device_map_3.xml6
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java24
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java42
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionServiceTest.java67
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java64
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java34
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java30
-rw-r--r--services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java104
-rw-r--r--services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java227
-rw-r--r--services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java53
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java81
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java47
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java37
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java127
-rw-r--r--services/tests/wmtests/AndroidManifest.xml6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTests.java187
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java131
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java7
-rw-r--r--telecomm/java/android/telecom/CallStreamingService.java26
-rw-r--r--telephony/java/android/telephony/AnomalyReporter.java2
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java24
-rw-r--r--telephony/java/android/telephony/TelephonyScanManager.java5
-rw-r--r--telephony/java/android/telephony/UiccCardInfo.java2
-rw-r--r--telephony/java/android/telephony/UiccSlotInfo.java13
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt5
479 files changed, 10593 insertions, 3058 deletions
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 051dde01c64e..b732da29b754 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -127,6 +127,7 @@ public class UserLifecycleTests {
private BroadcastWaiter mBroadcastWaiter;
private UserSwitchWaiter mUserSwitchWaiter;
private String mUserSwitchTimeoutMs;
+ private String mDisableUserSwitchingDialogAnimations;
private final BenchmarkRunner mRunner = new BenchmarkRunner();
@Rule
@@ -153,16 +154,17 @@ public class UserLifecycleTests {
Log.w(TAG, "WARNING: Tests are being run from user " + mAm.getCurrentUser()
+ " rather than the system user");
}
- mUserSwitchTimeoutMs = setSystemProperty("debug.usercontroller.user_switch_timeout_ms",
- "100000");
- if (TextUtils.isEmpty(mUserSwitchTimeoutMs)) {
- mUserSwitchTimeoutMs = "invalid";
- }
+ mUserSwitchTimeoutMs = setSystemProperty(
+ "debug.usercontroller.user_switch_timeout_ms", "100000");
+ mDisableUserSwitchingDialogAnimations = setSystemProperty(
+ "debug.usercontroller.disable_user_switching_dialog_animations", "true");
}
@After
public void tearDown() throws Exception {
setSystemProperty("debug.usercontroller.user_switch_timeout_ms", mUserSwitchTimeoutMs);
+ setSystemProperty("debug.usercontroller.disable_user_switching_dialog_animations",
+ mDisableUserSwitchingDialogAnimations);
mBroadcastWaiter.close();
mUserSwitchWaiter.close();
for (int userId : mUsersToRemove) {
@@ -1538,7 +1540,7 @@ public class UserLifecycleTests {
private String setSystemProperty(String name, String value) throws Exception {
final String oldValue = ShellHelper.runShellCommand("getprop " + name);
assertEquals("", ShellHelper.runShellCommand("setprop " + name + " " + value));
- return oldValue;
+ return TextUtils.firstNotEmpty(oldValue, "invalid");
}
private void waitForBroadcastIdle() {
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 3772960e8ac4..df1b66612ea2 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -4873,8 +4873,7 @@ public class AlarmManagerService extends SystemService {
}
}
if (wakeupUids.size() > 0 && mBatteryStatsInternal != null) {
- mBatteryStatsInternal.noteCpuWakingActivity(
- BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM, nowELAPSED,
+ mBatteryStatsInternal.noteWakingAlarmBatch(nowELAPSED,
wakeupUids.toArray());
}
deliverAlarmsLocked(triggerList, nowELAPSED);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 9bbc4a67a79a..ae63816945e1 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2075,6 +2075,14 @@ package android.media.soundtrigger {
public final class SoundTriggerManager {
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public static android.media.soundtrigger.SoundTriggerInstrumentation attachInstrumentation(@NonNull java.util.concurrent.Executor, @NonNull android.media.soundtrigger.SoundTriggerInstrumentation.GlobalCallback);
+ method @NonNull public android.media.soundtrigger.SoundTriggerManager createManagerForModule(@NonNull android.hardware.soundtrigger.SoundTrigger.ModuleProperties);
+ method @NonNull public android.media.soundtrigger.SoundTriggerManager createManagerForTestModule();
+ method @NonNull public static java.util.List<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> listModuleProperties();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int loadSoundModel(@NonNull android.hardware.soundtrigger.SoundTrigger.SoundModel);
+ }
+
+ public static class SoundTriggerManager.Model {
+ method @NonNull public android.hardware.soundtrigger.SoundTrigger.SoundModel getSoundModel();
}
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 08a1af47ee64..3d4b6bf019cb 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -209,10 +209,10 @@ import java.util.function.Consumer;
* The overlay will maintain the same relative position within the window bounds as the window
* moves. The overlay will also maintain the same relative position within the window bounds if
* the window is resized.
- * To attach an overlay to a window, use {@link attachAccessibilityOverlayToWindow}.
+ * To attach an overlay to a window, use {@link #attachAccessibilityOverlayToWindow}.
* Attaching an overlay to the display means that the overlay is independent of the active
* windows on that display.
- * To attach an overlay to a display, use {@link attachAccessibilityOverlayToDisplay}. </p>
+ * To attach an overlay to a display, use {@link #attachAccessibilityOverlayToDisplay}. </p>
* <p> When positioning an overlay that is attached to a window, the service must use window
* coordinates. In order to position an overlay on top of an existing UI element it is necessary
* to know the bounds of that element in window coordinates. To find the bounds in window
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 808f25eb7cd9..d4a96b40bb4c 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -1018,7 +1018,7 @@ public class AccessibilityServiceInfo implements Parcelable {
*
* @param motionEventSources A bit mask of {@link android.view.InputDevice} sources.
* @see AccessibilityService#onMotionEvent
- * @see MotionEventSources
+ * @see #MotionEventSources
*/
public void setMotionEventSources(@MotionEventSources int motionEventSources) {
mMotionEventSources = motionEventSources;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 3312294865d6..9e59ee496de1 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2547,7 +2547,7 @@ public class AppOpsManager {
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
new AppOpInfo.Builder(OP_TURN_SCREEN_ON, OPSTR_TURN_SCREEN_ON, "TURN_SCREEN_ON")
.setPermission(Manifest.permission.TURN_SCREEN_ON)
- .setDefaultMode(AppOpsManager.MODE_ERRORED).build(),
+ .setDefaultMode(AppOpsManager.MODE_DEFAULT).build(),
new AppOpInfo.Builder(OP_GET_ACCOUNTS, OPSTR_GET_ACCOUNTS, "GET_ACCOUNTS")
.setPermission(Manifest.permission.GET_ACCOUNTS)
.setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index d62e15a94f89..3249b41da934 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -174,6 +174,9 @@ interface IActivityTaskManager {
ActivityTaskManager.RootTaskInfo getFocusedRootTaskInfo();
Rect getTaskBounds(int taskId);
+ /** Focuses the top task on a display if it isn't already focused. Used for Recents. */
+ void focusTopTask(int displayId);
+
void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES)")
void updateLockTaskPackages(int userId, in String[] packages);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index bf695311e89e..b5efb73225d6 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1668,10 +1668,13 @@ public final class LoadedApk {
static final class ReceiverDispatcher {
final static class InnerReceiver extends IIntentReceiver.Stub {
+ final IApplicationThread mApplicationThread;
final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
final LoadedApk.ReceiverDispatcher mStrongRef;
- InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
+ InnerReceiver(IApplicationThread thread, LoadedApk.ReceiverDispatcher rd,
+ boolean strong) {
+ mApplicationThread = thread;
mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
mStrongRef = strong ? rd : null;
}
@@ -1718,7 +1721,8 @@ public final class LoadedApk {
if (extras != null) {
extras.setAllowFds(false);
}
- mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
+ mgr.finishReceiver(mApplicationThread.asBinder(), resultCode, data,
+ extras, false, intent.getFlags());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1825,7 +1829,7 @@ public final class LoadedApk {
}
mAppThread = appThread;
- mIIntentReceiver = new InnerReceiver(this, !registered);
+ mIIntentReceiver = new InnerReceiver(mAppThread, this, !registered);
mReceiver = receiver;
mContext = context;
mActivityThread = activityThread;
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 746dcb6f2e13..d8cedb8fa5d2 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -44,6 +44,7 @@ import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -246,6 +247,7 @@ public final class NotificationChannel implements Parcelable {
private boolean mBypassDnd;
private int mLockscreenVisibility = DEFAULT_VISIBILITY;
private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI;
+ private boolean mSoundRestored = false;
private boolean mLights;
private int mLightColor = DEFAULT_LIGHT_COLOR;
private long[] mVibration;
@@ -929,8 +931,9 @@ public final class NotificationChannel implements Parcelable {
/**
* @hide
*/
- public void populateFromXmlForRestore(XmlPullParser parser, Context context) {
- populateFromXml(XmlUtils.makeTyped(parser), true, context);
+ public void populateFromXmlForRestore(XmlPullParser parser, boolean pkgInstalled,
+ Context context) {
+ populateFromXml(XmlUtils.makeTyped(parser), true, pkgInstalled, context);
}
/**
@@ -938,14 +941,14 @@ public final class NotificationChannel implements Parcelable {
*/
@SystemApi
public void populateFromXml(XmlPullParser parser) {
- populateFromXml(XmlUtils.makeTyped(parser), false, null);
+ populateFromXml(XmlUtils.makeTyped(parser), false, true, null);
}
/**
* If {@param forRestore} is true, {@param Context} MUST be non-null.
*/
private void populateFromXml(TypedXmlPullParser parser, boolean forRestore,
- @Nullable Context context) {
+ boolean pkgInstalled, @Nullable Context context) {
Preconditions.checkArgument(!forRestore || context != null,
"forRestore is true but got null context");
@@ -956,7 +959,8 @@ public final class NotificationChannel implements Parcelable {
setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY));
Uri sound = safeUri(parser, ATT_SOUND);
- setSound(forRestore ? restoreSoundUri(context, sound) : sound, safeAudioAttributes(parser));
+ setSound(forRestore ? restoreSoundUri(context, sound, pkgInstalled) : sound,
+ safeAudioAttributes(parser));
enableLights(safeBool(parser, ATT_LIGHTS, false));
setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR));
@@ -978,8 +982,58 @@ public final class NotificationChannel implements Parcelable {
setImportantConversation(safeBool(parser, ATT_IMP_CONVERSATION, false));
}
+ /**
+ * Returns whether the sound for this channel was successfully restored
+ * from backup.
+ * @return false if the sound was not restored successfully. true otherwise (default value)
+ * @hide
+ */
+ public boolean isSoundRestored() {
+ return mSoundRestored;
+ }
+
@Nullable
- private Uri restoreSoundUri(Context context, @Nullable Uri uri) {
+ private Uri getCanonicalizedSoundUri(ContentResolver contentResolver, @NonNull Uri uri) {
+ if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(uri)) {
+ return uri;
+ }
+
+ if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) {
+ try {
+ contentResolver.getResourceId(uri);
+ return uri;
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ }
+
+ if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
+ return uri;
+ }
+
+ return contentResolver.canonicalize(uri);
+ }
+
+ @Nullable
+ private Uri getUncanonicalizedSoundUri(ContentResolver contentResolver, @NonNull Uri uri) {
+ if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(uri)
+ || ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())
+ || ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
+ return uri;
+ }
+ return contentResolver.uncanonicalize(uri);
+ }
+
+ /**
+ * Restore/validate sound Uri from backup
+ * @param context The Context
+ * @param uri The sound Uri to restore
+ * @param pkgInstalled If the parent package is installed
+ * @return restored and validated Uri
+ * @hide
+ */
+ @Nullable
+ public Uri restoreSoundUri(Context context, @Nullable Uri uri, boolean pkgInstalled) {
if (uri == null || Uri.EMPTY.equals(uri)) {
return null;
}
@@ -991,12 +1045,22 @@ public final class NotificationChannel implements Parcelable {
// the uri and in the case of not having the resource we end up with the default - better
// than broken. As a side effect we'll canonicalize already canonicalized uris, this is fine
// according to the docs because canonicalize method has to handle canonical uris as well.
- Uri canonicalizedUri = contentResolver.canonicalize(uri);
+ Uri canonicalizedUri = getCanonicalizedSoundUri(contentResolver, uri);
if (canonicalizedUri == null) {
- // We got a null because the uri in the backup does not exist here, so we return default
- return Settings.System.DEFAULT_NOTIFICATION_URI;
+ // Uri failed to restore with package installed
+ if (!mSoundRestored && pkgInstalled) {
+ mSoundRestored = true;
+ // We got a null because the uri in the backup does not exist here, so we return
+ // default
+ return Settings.System.DEFAULT_NOTIFICATION_URI;
+ } else {
+ // Flag as unrestored and try again later (on package install)
+ mSoundRestored = false;
+ return uri;
+ }
}
- return contentResolver.uncanonicalize(canonicalizedUri);
+ mSoundRestored = true;
+ return getUncanonicalizedSoundUri(contentResolver, canonicalizedUri);
}
/**
@@ -1019,7 +1083,7 @@ public final class NotificationChannel implements Parcelable {
if (sound == null || Uri.EMPTY.equals(sound)) {
return null;
}
- Uri canonicalSound = context.getContentResolver().canonicalize(sound);
+ Uri canonicalSound = getCanonicalizedSoundUri(context.getContentResolver(), sound);
if (canonicalSound == null) {
// The content provider does not support canonical uris so we backup the default
return Settings.System.DEFAULT_NOTIFICATION_URI;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a8a2ad1bb8df..7b6835792370 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8394,8 +8394,7 @@ public class DevicePolicyManager {
* <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA} to be able to call this method; if it has
- * not, a security exception will be thrown, or the caller must hold the permission
- * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CAMERA}.
+ * not, a security exception will be thrown.
* <p>
* <b>Note</b>, this policy type is deprecated for legacy device admins since
* {@link android.os.Build.VERSION_CODES#Q}. On Android
@@ -8411,8 +8410,7 @@ public class DevicePolicyManager {
the caller is not a device admin
* @param disabled Whether or not the camera should be disabled.
* @throws SecurityException if {@code admin} is not an active administrator or does not use
- * {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA} and the caller does not hold
- * the permisisons {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CAMERA}.
+ * {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA}.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_CAMERA, conditional = true)
public void setCameraDisabled(@Nullable ComponentName admin, boolean disabled) {
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 31c02b886686..fa99b59888f5 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -2085,7 +2085,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
*
* @param uri The URI whose file is to be opened.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
*
* @return Returns a new ParcelFileDescriptor which you can use to access
* the file.
@@ -2147,7 +2148,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
*
* @param uri The URI whose file is to be opened.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
* @param signal A signal to cancel the operation in progress, or
* {@code null} if none. For example, if you are downloading a
* file from the network to service a "rw" mode request, you
@@ -2208,7 +2210,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
*
* @param uri The URI whose file is to be opened.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
*
* @return Returns a new AssetFileDescriptor which you can use to access
* the file.
@@ -2262,7 +2265,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
*
* @param uri The URI whose file is to be opened.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
* @param signal A signal to cancel the operation in progress, or
* {@code null} if none. For example, if you are downloading a
* file from the network to service a "rw" mode request, you
@@ -2294,7 +2298,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
*
* @param uri The URI to be opened.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
*
* @return Returns a new ParcelFileDescriptor that can be used by the
* client to access the file.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index feca7a022934..b2cd7e90f291 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1536,7 +1536,8 @@ public abstract class ContentResolver implements ContentInterface {
/**
* Synonym for {@link #openOutputStream(Uri, String)
- * openOutputStream(uri, "w")}.
+ * openOutputStream(uri, "w")}. Please note the implementation of "w" is up to each
+ * Provider implementation and it may or may not truncate.
*
* @param uri The desired URI.
* @return an OutputStream or {@code null} if the provider recently crashed.
@@ -1562,7 +1563,8 @@ public abstract class ContentResolver implements ContentInterface {
*
* @param uri The desired URI.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
* @return an OutputStream or {@code null} if the provider recently crashed.
* @throws FileNotFoundException if the provided URI could not be opened.
* @see #openAssetFileDescriptor(Uri, String)
@@ -1619,7 +1621,8 @@ public abstract class ContentResolver implements ContentInterface {
*
* @param uri The desired URI to open.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
* @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
* provider recently crashed. You own this descriptor and are responsible for closing it
* when done.
@@ -1662,7 +1665,8 @@ public abstract class ContentResolver implements ContentInterface {
*
* @param uri The desired URI to open.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
* @param cancellationSignal A signal to cancel the operation in progress,
* or null if none. If the operation is canceled, then
* {@link OperationCanceledException} will be thrown.
@@ -1756,7 +1760,8 @@ public abstract class ContentResolver implements ContentInterface {
*
* @param uri The desired URI to open.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
* @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
* provider recently crashed. You own this descriptor and are responsible for closing it
* when done.
@@ -1810,7 +1815,8 @@ public abstract class ContentResolver implements ContentInterface {
*
* @param uri The desired URI to open.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note "w" is write only and "wt" is write and truncate.
+ * See{@link ParcelFileDescriptor#parseMode} for more details.
* @param cancellationSignal A signal to cancel the operation in progress, or null if
* none. If the operation is canceled, then
* {@link OperationCanceledException} will be thrown.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2b73afcd740f..c221d724c5a2 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -7613,7 +7613,7 @@ public abstract class Context {
* the device association is changed by the system.
* <p>
* The callback can be called when an app is moved to a different device and the {@code Context}
- * is not explicily associated with a specific device.
+ * is not explicitly associated with a specific device.
* </p>
* <p> When an application receives a device id update callback, this Context is guaranteed to
* also have an updated display ID(if any) and {@link Configuration}.
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
index 7701125780f4..875550aea5f5 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
@@ -46,6 +46,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
public class CameraExtensionJpegProcessor implements ICaptureProcessorImpl {
public final static String TAG = "CameraExtensionJpeg";
private final static int JPEG_QUEUE_SIZE = 1;
+ private final static int JPEG_APP_SEGMENT_SIZE = 64 * 1024;
private final Handler mHandler;
private final HandlerThread mHandlerThread;
@@ -243,9 +244,10 @@ public class CameraExtensionJpegProcessor implements ICaptureProcessorImpl {
private void initializePipeline() throws RemoteException {
if ((mFormat != -1) && (mOutputSurface != null) && (mResolution != null) &&
(mYuvReader == null)) {
- // Jpeg/blobs are expected to be configured with (w*h)x1
+ // Jpeg/blobs are expected to be configured with (w*h)x1.5 + 64k Jpeg APP1 segment
mOutputWriter = ImageWriter.newInstance(mOutputSurface, 1 /*maxImages*/,
- ImageFormat.JPEG, mResolution.width * mResolution.height, 1);
+ ImageFormat.JPEG,
+ (mResolution.width * mResolution.height * 3)/2 + JPEG_APP_SEGMENT_SIZE, 1);
mYuvReader = ImageReader.newInstance(mResolution.width, mResolution.height, mFormat,
JPEG_QUEUE_SIZE);
mYuvReader.setOnImageAvailableListener(
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index e0af9137db25..02212968cdb0 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -44,6 +44,7 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Slog;
import android.view.Surface;
@@ -127,6 +128,11 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
@Override // binder call
public void onRemoved(Face face, int remaining) {
mHandler.obtainMessage(MSG_REMOVED, remaining, 0, face).sendToTarget();
+ if (remaining == 0) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0,
+ UserHandle.USER_CURRENT);
+ }
}
@Override
@@ -342,6 +348,13 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
return;
}
+ if (hardwareAuthToken == null) {
+ callback.onEnrollmentError(FACE_ERROR_UNABLE_TO_PROCESS,
+ getErrorString(mContext, FACE_ERROR_UNABLE_TO_PROCESS,
+ 0 /* vendorCode */));
+ return;
+ }
+
if (getEnrolledFaces(userId).size()
>= mContext.getResources().getInteger(R.integer.config_faceMaxTemplatesPerUser)) {
callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index eb8136e39d29..01977f6195ff 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -712,6 +712,13 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
return;
}
+ if (hardwareAuthToken == null) {
+ callback.onEnrollmentError(FINGERPRINT_ERROR_UNABLE_TO_PROCESS,
+ getErrorString(mContext, FINGERPRINT_ERROR_UNABLE_TO_PROCESS,
+ 0 /* vendorCode */));
+ return;
+ }
+
if (mService != null) {
try {
mEnrollmentCallback = callback;
@@ -832,7 +839,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
- * Removes all face templates for the given user.
+ * Removes all fingerprint templates for the given user.
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index e1662b81b017..b4fe3a2a249c 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -588,10 +588,10 @@ public final class UsbPortStatus implements Parcelable {
* Returns non compliant reasons, if any, for the connected
* charger/cable/accessory/USB port.
*
- * @return array including {@link #NON_COMPLIANT_REASON_DEBUG_ACCESSORY},
- * {@link #NON_COMPLIANT_REASON_BC12},
- * {@link #NON_COMPLIANT_REASON_MISSING_RP},
- * or {@link #NON_COMPLIANT_REASON_TYPEC}
+ * @return array including {@link #COMPLIANCE_WARNING_OTHER},
+ * {@link #COMPLIANCE_WARNING_DEBUG_ACCESSORY},
+ * {@link #COMPLIANCE_WARNING_BC_1_2},
+ * or {@link #COMPLIANCE_WARNING_MISSING_RP}
*/
@CheckResult
@NonNull
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index a52e3d49148d..2c31e32f2ef8 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -213,6 +213,13 @@ public class GraphicsEnvironment {
}
/**
+ * Switch the system to use ANGLE as the default GLES driver.
+ */
+ public void toggleAngleAsSystemDriver(boolean enabled) {
+ nativeToggleAngleAsSystemDriver(enabled);
+ }
+
+ /**
* Query to determine if the Game Mode has enabled ANGLE.
*/
private boolean isAngleEnabledByGameMode(Context context, String packageName) {
@@ -992,6 +999,7 @@ public class GraphicsEnvironment {
String appPackage, boolean angleIsSystemDriver, String legacyDriverName);
private static native boolean getShouldUseAngle(String packageName);
private static native boolean setInjectLayersPrSetDumpable();
+ private static native void nativeToggleAngleAsSystemDriver(boolean enabled);
/**
* Hint for GraphicsEnvironment that an activity is launching on the process.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 561f7982c1de..3487b013fa0b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3037,14 +3037,23 @@ public final class Settings {
public void destroy() {
try {
// If this process is the system server process, mArray is the same object as
- // the memory int array kept inside SetingsProvider, so skipping the close()
- if (!Settings.isInSystemServer()) {
+ // the memory int array kept inside SettingsProvider, so skipping the close()
+ if (!Settings.isInSystemServer() && !mArray.isClosed()) {
mArray.close();
}
} catch (IOException e) {
Log.e(TAG, "Error closing backing array", e);
}
}
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ destroy();
+ } finally {
+ super.finalize();
+ }
+ }
}
private static final class ContentProviderHolder {
@@ -4668,22 +4677,16 @@ public final class Settings {
"display_color_mode_vendor_hint";
/**
- * The user selected min refresh rate in frames per second.
- *
- * If this isn't set, 0 will be used.
+ * Whether or not the peak refresh rate should be forced. 0=no, 1=yes
* @hide
*/
- @Readable
- public static final String MIN_REFRESH_RATE = "min_refresh_rate";
+ public static final String FORCE_PEAK_REFRESH_RATE = "force_peak_refresh_rate";
/**
- * The user selected peak refresh rate in frames per second.
- *
- * If this isn't set, the system falls back to a device specific default.
+ * Whether or not the peak refresh rate should be used for some content. 0=no, 1=yes
* @hide
*/
- @Readable
- public static final String PEAK_REFRESH_RATE = "peak_refresh_rate";
+ public static final String SMOOTH_DISPLAY = "smooth_display";
/**
* The amount of time in milliseconds before the device goes to sleep or begins
@@ -10193,9 +10196,7 @@ public final class Settings {
*
* Face unlock re enroll.
* 0 = No re enrollment.
- * 1 = Re enrollment is suggested.
- * 2 = Re enrollment is required after a set time period.
- * 3 = Re enrollment is required immediately.
+ * 1 = Re enrollment is required.
*
* @hide
*/
diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java
index 82571db469be..e9bb28c252e0 100644
--- a/core/java/android/service/dreams/DreamManagerInternal.java
+++ b/core/java/android/service/dreams/DreamManagerInternal.java
@@ -84,6 +84,19 @@ public abstract class DreamManagerInternal {
*
* @param keepDreaming True if the current dream should continue when undocking.
*/
- void onKeepDreamingWhenUnpluggingChanged(boolean keepDreaming);
+ default void onKeepDreamingWhenUnpluggingChanged(boolean keepDreaming) {
+ }
+
+ /**
+ * Called when dreaming has started.
+ */
+ default void onDreamingStarted() {
+ }
+
+ /**
+ * Called when dreaming has stopped.
+ */
+ default void onDreamingStopped() {
+ }
}
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 9d3d70d4a8f7..8d84e4413635 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -2738,7 +2738,12 @@ public abstract class WallpaperService extends Service {
engineWrapper.destroy();
}
mActiveEngines.clear();
- mBackgroundThread.quitSafely();
+ if (mBackgroundThread != null) {
+ // onDestroy might be called without a previous onCreate if WallpaperService was
+ // instantiated manually. While this is a misuse of the API, some things break
+ // if here we don't take into consideration this scenario.
+ mBackgroundThread.quitSafely();
+ }
Trace.endSection();
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index baefd853876b..60529c72afae 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -2188,7 +2188,7 @@ public final class Display {
*/
@NonNull
public float[] getAlternativeRefreshRates() {
- return mAlternativeRefreshRates;
+ return Arrays.copyOf(mAlternativeRefreshRates, mAlternativeRefreshRates.length);
}
/**
@@ -2197,7 +2197,7 @@ public final class Display {
@NonNull
@HdrCapabilities.HdrType
public int[] getSupportedHdrTypes() {
- return mSupportedHdrTypes;
+ return Arrays.copyOf(mSupportedHdrTypes, mSupportedHdrTypes.length);
}
/**
@@ -2497,8 +2497,10 @@ public final class Display {
* @deprecated use {@link Display#getMode()}
* and {@link Mode#getSupportedHdrTypes()} instead
*/
- public @HdrType int[] getSupportedHdrTypes() {
- return mSupportedHdrTypes;
+ @Deprecated
+ @HdrType
+ public int[] getSupportedHdrTypes() {
+ return Arrays.copyOf(mSupportedHdrTypes, mSupportedHdrTypes.length);
}
/**
* Returns the desired content max luminance data in cd/m2 for this display.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index e31adcfd699e..f2373fbfa858 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -341,6 +341,9 @@ public final class DisplayInfo implements Parcelable {
@Nullable
public DisplayShape displayShape;
+ /**
+ * Refresh rate range limitation based on the current device layout
+ */
@Nullable
public SurfaceControl.RefreshRateRange layoutLimitedRefreshRate;
@@ -354,7 +357,7 @@ public final class DisplayInfo implements Parcelable {
* RefreshRateRange limitation for @Temperature.ThrottlingStatus
*/
@NonNull
- public SparseArray<SurfaceControl.RefreshRateRange> refreshRateThermalThrottling =
+ public SparseArray<SurfaceControl.RefreshRateRange> thermalRefreshRateThrottling =
new SparseArray<>();
public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
@@ -434,7 +437,7 @@ public final class DisplayInfo implements Parcelable {
&& Objects.equals(displayShape, other.displayShape)
&& Objects.equals(layoutLimitedRefreshRate, other.layoutLimitedRefreshRate)
&& BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio)
- && refreshRateThermalThrottling.contentEquals(other.refreshRateThermalThrottling);
+ && thermalRefreshRateThrottling.contentEquals(other.thermalRefreshRateThrottling);
}
@Override
@@ -491,7 +494,7 @@ public final class DisplayInfo implements Parcelable {
displayShape = other.displayShape;
layoutLimitedRefreshRate = other.layoutLimitedRefreshRate;
hdrSdrRatio = other.hdrSdrRatio;
- refreshRateThermalThrottling = other.refreshRateThermalThrottling;
+ thermalRefreshRateThrottling = other.thermalRefreshRateThrottling;
}
public void readFromParcel(Parcel source) {
@@ -554,7 +557,7 @@ public final class DisplayInfo implements Parcelable {
displayShape = source.readTypedObject(DisplayShape.CREATOR);
layoutLimitedRefreshRate = source.readTypedObject(SurfaceControl.RefreshRateRange.CREATOR);
hdrSdrRatio = source.readFloat();
- refreshRateThermalThrottling = source.readSparseArray(null,
+ thermalRefreshRateThrottling = source.readSparseArray(null,
SurfaceControl.RefreshRateRange.class);
}
@@ -616,7 +619,7 @@ public final class DisplayInfo implements Parcelable {
dest.writeTypedObject(displayShape, flags);
dest.writeTypedObject(layoutLimitedRefreshRate, flags);
dest.writeFloat(hdrSdrRatio);
- dest.writeSparseArray(refreshRateThermalThrottling);
+ dest.writeSparseArray(thermalRefreshRateThrottling);
}
@Override
@@ -884,8 +887,8 @@ public final class DisplayInfo implements Parcelable {
} else {
sb.append(hdrSdrRatio);
}
- sb.append(", refreshRateThermalThrottling ");
- sb.append(refreshRateThermalThrottling);
+ sb.append(", thermalRefreshRateThrottling ");
+ sb.append(thermalRefreshRateThrottling);
sb.append("}");
return sb.toString();
}
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 77f3b1da92b4..dd4f9644da96 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.view.inputmethod.InputMethodManager;
+import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
@@ -305,6 +306,9 @@ public class HandwritingInitiator {
mImm.startStylusHandwriting(view);
mState.mHasInitiatedHandwriting = true;
mState.mShouldInitHandwriting = false;
+ if (view instanceof TextView) {
+ ((TextView) view).hideHint();
+ }
}
/**
@@ -323,6 +327,9 @@ public class HandwritingInitiator {
mState.mHasInitiatedHandwriting = true;
mState.mShouldInitHandwriting = false;
}
+ if (view instanceof TextView) {
+ ((TextView) view).hideHint();
+ }
return true;
}
return false;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index bc6a3b540ce7..2fa03c5ba92e 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -220,6 +220,7 @@ public final class SurfaceControl implements Parcelable {
long newParentNativeObject);
private static native void nativeSetBuffer(long transactionObj, long nativeObject,
HardwareBuffer buffer, long fencePtr, Consumer<SyncFence> releaseCallback);
+ private static native void nativeUnsetBuffer(long transactionObj, long nativeObject);
private static native void nativeSetBufferTransform(long transactionObj, long nativeObject,
int transform);
private static native void nativeSetDataSpace(long transactionObj, long nativeObject,
@@ -3664,6 +3665,22 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Unsets the buffer for the SurfaceControl in the current Transaction. This will not clear
+ * the buffer being rendered, but resets the buffer state in the Transaction only. The call
+ * will also invoke the release callback.
+ *
+ * Note, this call is different from passing a null buffer to
+ * {@link SurfaceControl.Transaction#setBuffer} which will release the last displayed
+ * buffer.
+ *
+ * @hide
+ */
+ public Transaction unsetBuffer(SurfaceControl sc) {
+ nativeUnsetBuffer(mNativeObject, sc.mNativeObject);
+ return this;
+ }
+
+ /**
* Updates the HardwareBuffer displayed for the SurfaceControl.
*
* Note that the buffer must be allocated with {@link HardwareBuffer#USAGE_COMPOSER_OVERLAY}
@@ -3682,7 +3699,8 @@ public final class SurfaceControl implements Parcelable {
* until all presentation fences have signaled, ensuring the transaction remains consistent.
*
* @param sc The SurfaceControl to update
- * @param buffer The buffer to be displayed
+ * @param buffer The buffer to be displayed. Pass in a null buffer to release the last
+ * displayed buffer.
* @param fence The presentation fence. If null or invalid, this is equivalent to
* {@link #setBuffer(SurfaceControl, HardwareBuffer)}
* @return this
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2b29e7878a5d..6bd9538a9f65 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -885,6 +885,16 @@ public final class ViewRootImpl implements ViewParent,
*/
private SurfaceSyncGroup mActiveSurfaceSyncGroup;
+
+ private final Object mPreviousSyncSafeguardLock = new Object();
+
+ /**
+ * Wraps the TransactionCommitted callback for the previous SSG so it can be added to the next
+ * SSG if started before previous has completed.
+ */
+ @GuardedBy("mPreviousSyncSafeguardLock")
+ private SurfaceSyncGroup mPreviousSyncSafeguard;
+
private static final Object sSyncProgressLock = new Object();
// The count needs to be static since it's used to enable or disable RT animations which is
// done at a global level per process. If any VRI syncs are in progress, we can't enable RT
@@ -11329,6 +11339,61 @@ public final class ViewRootImpl implements ViewParent,
});
}
+ /**
+ * This code will ensure that if multiple SurfaceSyncGroups are created for the same
+ * ViewRootImpl the SurfaceSyncGroups will maintain an order. The scenario that could occur
+ * is the following:
+ * <p>
+ * 1. SSG1 is created that includes the target VRI. There could be other VRIs in SSG1
+ * 2. The target VRI draws its frame and marks its own active SSG as ready, but SSG1 is still
+ * waiting on other things in the SSG
+ * 3. Another SSG2 is created for the target VRI. The second frame renders and marks its own
+ * second SSG as complete. SSG2 has nothing else to wait on, so it will apply at this point,
+ * even though SSG1 has not finished.
+ * 4. Frame2 will get to SF first and Frame1 will later get to SF when SSG1 completes.
+ * <p>
+ * The code below ensures the SSGs that contains the VRI maintain an order. We create a new SSG
+ * that's a safeguard SSG. Its only job is to prevent the next active SSG from completing.
+ * The current active SSG for VRI will add a transaction committed callback and when that's
+ * invoked, it will mark the safeguard SSG as ready. If a new request to create a SSG comes
+ * in and the safeguard SSG is not null, it's added as part of the new active SSG. A new
+ * safeguard SSG is created to correspond to the new active SSG. This creates a chain to
+ * ensure the latter SSG always waits for the former SSG's transaction to get to SF.
+ */
+ private void safeguardOverlappingSyncs(SurfaceSyncGroup activeSurfaceSyncGroup) {
+ SurfaceSyncGroup safeguardSsg = new SurfaceSyncGroup("VRI-Safeguard");
+ // Always disable timeout on the safeguard sync
+ safeguardSsg.toggleTimeout(false /* enable */);
+ synchronized (mPreviousSyncSafeguardLock) {
+ if (mPreviousSyncSafeguard != null) {
+ activeSurfaceSyncGroup.add(mPreviousSyncSafeguard, null /* runnable */);
+ // Temporarily disable the timeout on the SSG that will contain the buffer. This
+ // is to ensure we don't timeout the active SSG before the previous one completes to
+ // ensure the order is maintained. The previous SSG has a timeout on its own SSG
+ // so it's guaranteed to complete.
+ activeSurfaceSyncGroup.toggleTimeout(false /* enable */);
+ mPreviousSyncSafeguard.addSyncCompleteCallback(mSimpleExecutor, () -> {
+ // Once we receive that the previous sync guard has been invoked, we can re-add
+ // the timeout on the active sync to ensure we eventually complete so it's not
+ // stuck permanently.
+ activeSurfaceSyncGroup.toggleTimeout(true /*enable */);
+ });
+ }
+ mPreviousSyncSafeguard = safeguardSsg;
+ }
+
+ Transaction t = new Transaction();
+ t.addTransactionCommittedListener(mSimpleExecutor, () -> {
+ safeguardSsg.markSyncReady();
+ synchronized (mPreviousSyncSafeguardLock) {
+ if (mPreviousSyncSafeguard == safeguardSsg) {
+ mPreviousSyncSafeguard = null;
+ }
+ }
+ });
+ activeSurfaceSyncGroup.addTransaction(t);
+ }
+
@Override
public SurfaceSyncGroup getOrCreateSurfaceSyncGroup() {
boolean newSyncGroup = false;
@@ -11355,6 +11420,7 @@ public final class ViewRootImpl implements ViewParent,
mHandler.post(runnable);
}
});
+ safeguardOverlappingSyncs(mActiveSurfaceSyncGroup);
updateSyncInProgressCount(mActiveSurfaceSyncGroup);
newSyncGroup = true;
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index ddcb431ee06d..5b6df1cb754e 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -467,7 +467,7 @@ public interface WindowManager extends ViewManager {
* implementation.
* @hide
*/
- int TRANSIT_FIRST_CUSTOM = 13;
+ int TRANSIT_FIRST_CUSTOM = 1000;
/**
* @hide
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 13ac329815f5..fa0052cf664a 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -840,6 +840,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
mAnchorId = AccessibilityNodeInfo.UNDEFINED_NODE_ID;
mTitle = null;
mTransitionTime = 0;
+ mLocales = LocaleList.getEmptyLocaleList();
}
/**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3fbb50581b25..34fe935b55a0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -806,6 +806,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private CharSequence mHint;
@UnsupportedAppUsage
private Layout mHintLayout;
+ private boolean mHideHint;
private MovementMethod mMovement;
@@ -7180,6 +7181,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sendOnTextChanged(text, 0, oldlen, textLength);
onTextChanged(text, 0, oldlen, textLength);
+ mHideHint = false;
+
if (a11yTextChangeType == AccessibilityUtils.TEXT) {
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
@@ -7338,6 +7341,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private void setHintInternal(CharSequence hint) {
+ mHideHint = false;
mHint = TextUtils.stringOrSpannedString(hint);
if (mLayout != null) {
@@ -7379,6 +7383,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Temporarily hides the hint text until the text is modified, or the hint text is modified, or
+ * the view gains or loses focus.
+ *
+ * @hide
+ */
+ public void hideHint() {
+ if (isShowingHint()) {
+ mHideHint = true;
+ invalidate();
+ }
+ }
+
+ /**
* Returns if the text is constrained to a single horizontally scrolling line ignoring new
* line characters instead of letting it wrap onto multiple lines.
*
@@ -8974,7 +8991,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Layout layout = mLayout;
- if (mHint != null && mText.length() == 0) {
+ if (mHint != null && !mHideHint && mText.length() == 0) {
if (mHintTextColor != null) {
color = mCurHintTextColor;
}
@@ -11293,7 +11310,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private boolean isShowingHint() {
- return TextUtils.isEmpty(mText) && !TextUtils.isEmpty(mHint);
+ return TextUtils.isEmpty(mText) && !TextUtils.isEmpty(mHint) && !mHideHint;
}
/**
@@ -12437,6 +12454,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sendOnTextChanged(buffer, start, before, after);
onTextChanged(buffer, start, before, after);
+ mHideHint = false;
clearGesturePreviewHighlight();
}
@@ -12577,6 +12595,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return;
}
+ mHideHint = false;
+
if (mEditor != null) mEditor.onFocusChanged(focused, direction);
if (focused) {
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index b3d512488239..18405676dbd8 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -38,6 +38,7 @@ import android.view.SurfaceView;
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
@@ -63,7 +64,12 @@ public final class SurfaceSyncGroup {
private static final int MAX_COUNT = 100;
private static final AtomicInteger sCounter = new AtomicInteger(0);
- private static final int TRANSACTION_READY_TIMEOUT = 1000 * Build.HW_TIMEOUT_MULTIPLIER;
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public static final int TRANSACTION_READY_TIMEOUT = 1000 * Build.HW_TIMEOUT_MULTIPLIER;
private static Supplier<Transaction> sTransactionFactory = Transaction::new;
@@ -123,6 +129,14 @@ public final class SurfaceSyncGroup {
@GuardedBy("mLock")
private boolean mTimeoutAdded;
+ /**
+ * Disable the timeout for this SSG so it will never be set until there's an explicit call to
+ * add a timeout.
+ */
+ @GuardedBy("mLock")
+ private boolean mTimeoutDisabled;
+
+
private static boolean isLocalBinder(IBinder binder) {
return !(binder instanceof BinderProxy);
}
@@ -223,6 +237,10 @@ public final class SurfaceSyncGroup {
*/
public void addSyncCompleteCallback(Executor executor, Runnable runnable) {
synchronized (mLock) {
+ if (mFinished) {
+ executor.execute(runnable);
+ return;
+ }
mSyncCompleteCallbacks.add(new Pair<>(executor, runnable));
}
}
@@ -768,6 +786,21 @@ public final class SurfaceSyncGroup {
}
}
+ /**
+ * @hide
+ */
+ public void toggleTimeout(boolean enable) {
+ synchronized (mLock) {
+ mTimeoutDisabled = !enable;
+ if (mTimeoutAdded && !enable) {
+ mHandler.removeCallbacksAndMessages(this);
+ mTimeoutAdded = false;
+ } else if (!mTimeoutAdded && enable) {
+ addTimeout();
+ }
+ }
+ }
+
private void addTimeout() {
synchronized (sHandlerThreadLock) {
if (sHandlerThread == null) {
@@ -777,7 +810,7 @@ public final class SurfaceSyncGroup {
}
synchronized (mLock) {
- if (mTimeoutAdded) {
+ if (mTimeoutAdded || mTimeoutDisabled) {
// We only need one timeout for the entire SurfaceSyncGroup since we just want to
// ensure it doesn't stay stuck forever.
return;
diff --git a/core/java/android/window/TaskConstants.java b/core/java/android/window/TaskConstants.java
index 3a04198a3add..e18fd50dc38e 100644
--- a/core/java/android/window/TaskConstants.java
+++ b/core/java/android/window/TaskConstants.java
@@ -47,37 +47,31 @@ public class TaskConstants {
-2 * TASK_CHILD_LAYER_REGION_SIZE;
/**
- * When a unresizable app is moved in the different configuration, a restart button appears
- * allowing to adapt (~resize) app to the new configuration mocks.
+ * Compat UI components: reachability education, size compat restart
+ * button, letterbox education, restart dialog.
* @hide
*/
- public static final int TASK_CHILD_LAYER_SIZE_COMPAT_RESTART_BUTTON =
- TASK_CHILD_LAYER_REGION_SIZE;
+ public static final int TASK_CHILD_LAYER_COMPAT_UI = TASK_CHILD_LAYER_REGION_SIZE;
- /**
- * Shown the first time an app is opened in size compat mode in landscape.
- * @hide
- */
- public static final int TASK_CHILD_LAYER_LETTERBOX_EDUCATION = 2 * TASK_CHILD_LAYER_REGION_SIZE;
/**
* Captions, window frames and resize handlers around task windows.
* @hide
*/
- public static final int TASK_CHILD_LAYER_WINDOW_DECORATIONS = 3 * TASK_CHILD_LAYER_REGION_SIZE;
+ public static final int TASK_CHILD_LAYER_WINDOW_DECORATIONS = 2 * TASK_CHILD_LAYER_REGION_SIZE;
/**
* Overlays the task when going into PIP w/ gesture navigation.
* @hide
*/
public static final int TASK_CHILD_LAYER_RECENTS_ANIMATION_PIP_OVERLAY =
- 4 * TASK_CHILD_LAYER_REGION_SIZE;
+ 3 * TASK_CHILD_LAYER_REGION_SIZE;
/**
* Allows other apps to add overlays on the task (i.e. game dashboard)
* @hide
*/
- public static final int TASK_CHILD_LAYER_TASK_OVERLAY = 5 * TASK_CHILD_LAYER_REGION_SIZE;
+ public static final int TASK_CHILD_LAYER_TASK_OVERLAY = 4 * TASK_CHILD_LAYER_REGION_SIZE;
/**
* Z-orders of task child layers other than activities, task fragments and layers interleaved
@@ -87,8 +81,7 @@ public class TaskConstants {
@IntDef({
TASK_CHILD_LAYER_TASK_BACKGROUND,
TASK_CHILD_LAYER_LETTERBOX_BACKGROUND,
- TASK_CHILD_LAYER_SIZE_COMPAT_RESTART_BUTTON,
- TASK_CHILD_LAYER_LETTERBOX_EDUCATION,
+ TASK_CHILD_LAYER_COMPAT_UI,
TASK_CHILD_LAYER_WINDOW_DECORATIONS,
TASK_CHILD_LAYER_RECENTS_ANIMATION_PIP_OVERLAY,
TASK_CHILD_LAYER_TASK_OVERLAY
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 0f3eef7a3289..628fc3140ee8 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -152,8 +152,14 @@ public final class TransitionInfo implements Parcelable {
/** The task became the top-most task even if it didn't change visibility. */
public static final int FLAG_MOVED_TO_TOP = 1 << 20;
+ /**
+ * This transition must be the only transition when it starts (ie. it must wait for all other
+ * transition animations to finish).
+ */
+ public static final int FLAG_SYNC = 1 << 21;
+
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
- public static final int FLAG_FIRST_CUSTOM = 1 << 21;
+ public static final int FLAG_FIRST_CUSTOM = 1 << 22;
/** The change belongs to a window that won't contain activities. */
public static final int FLAGS_IS_NON_APP_WINDOW =
@@ -183,12 +189,14 @@ public final class TransitionInfo implements Parcelable {
FLAG_NO_ANIMATION,
FLAG_TASK_LAUNCHING_BEHIND,
FLAG_MOVED_TO_TOP,
+ FLAG_SYNC,
FLAG_FIRST_CUSTOM
})
public @interface ChangeFlags {}
private final @TransitionType int mType;
- private final @TransitionFlags int mFlags;
+ private @TransitionFlags int mFlags;
+ private int mTrack = 0;
private final ArrayList<Change> mChanges = new ArrayList<>();
private final ArrayList<Root> mRoots = new ArrayList<>();
@@ -210,6 +218,7 @@ public final class TransitionInfo implements Parcelable {
in.readTypedList(mRoots, Root.CREATOR);
mOptions = in.readTypedObject(AnimationOptions.CREATOR);
mDebugId = in.readInt();
+ mTrack = in.readInt();
}
@Override
@@ -221,6 +230,7 @@ public final class TransitionInfo implements Parcelable {
dest.writeTypedList(mRoots, flags);
dest.writeTypedObject(mOptions, flags);
dest.writeInt(mDebugId);
+ dest.writeInt(mTrack);
}
@NonNull
@@ -262,6 +272,10 @@ public final class TransitionInfo implements Parcelable {
return mType;
}
+ public void setFlags(int flags) {
+ mFlags = flags;
+ }
+
public int getFlags() {
return mFlags;
}
@@ -356,6 +370,16 @@ public final class TransitionInfo implements Parcelable {
return (mFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0;
}
+ /** Gets which animation track this transition should run on. */
+ public int getTrack() {
+ return mTrack;
+ }
+
+ /** Sets which animation track this transition should run on. */
+ public void setTrack(int track) {
+ mTrack = track;
+ }
+
/**
* Set an arbitrary "debug" id for this info. This id will not be used for any "real work",
* it is just for debugging and logging.
@@ -373,7 +397,8 @@ public final class TransitionInfo implements Parcelable {
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{id=").append(mDebugId).append(" t=").append(transitTypeToString(mType))
- .append(" f=0x").append(Integer.toHexString(mFlags)).append(" r=[");
+ .append(" f=0x").append(Integer.toHexString(mFlags)).append(" trk=").append(mTrack)
+ .append(" r=[");
for (int i = 0; i < mRoots.size(); ++i) {
if (i > 0) {
sb.append(',');
@@ -461,6 +486,9 @@ public final class TransitionInfo implements Parcelable {
if ((flags & FLAG_TASK_LAUNCHING_BEHIND) != 0) {
sb.append((sb.length() == 0 ? "" : "|") + "TASK_LAUNCHING_BEHIND");
}
+ if ((flags & FLAG_SYNC) != 0) {
+ sb.append((sb.length() == 0 ? "" : "|") + "SYNC");
+ }
if ((flags & FLAG_FIRST_CUSTOM) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM");
}
@@ -532,6 +560,7 @@ public final class TransitionInfo implements Parcelable {
*/
public TransitionInfo localRemoteCopy() {
final TransitionInfo out = new TransitionInfo(mType, mFlags);
+ out.mTrack = mTrack;
out.mDebugId = mDebugId;
for (int i = 0; i < mChanges.size(); ++i) {
out.mChanges.add(mChanges.get(i).localRemoteCopy());
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index d4ff794ea5ec..57cc38cc6dfd 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -57,6 +57,8 @@ public class AssistUtils {
public static final int INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS = 5;
/** value for INVOCATION_TYPE_KEY: long press on physical power button */
public static final int INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS = 6;
+ /** value for INVOCATION_TYPE_KEY: press on physcial assistant button */
+ public static final int INVOCATION_TYPE_ASSIST_BUTTON = 7;
private final Context mContext;
private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
new file mode 100644
index 000000000000..39d8380c7e95
--- /dev/null
+++ b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
@@ -0,0 +1,92 @@
+/*
+ * 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.internal.display;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.Display;
+
+/**
+ * Constants and utility methods for refresh rate settings.
+ */
+public class RefreshRateSettingsUtils {
+
+ private static final String TAG = "RefreshRateSettingsUtils";
+
+ public static final float DEFAULT_REFRESH_RATE = 60f;
+
+ /**
+ * Find the highest refresh rate among all the modes of the default display.
+ * @param context The context
+ * @return The highest refresh rate
+ */
+ public static float findHighestRefreshRateForDefaultDisplay(Context context) {
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
+
+ if (display == null) {
+ Log.w(TAG, "No valid default display device");
+ return DEFAULT_REFRESH_RATE;
+ }
+
+ float maxRefreshRate = DEFAULT_REFRESH_RATE;
+ for (Display.Mode mode : display.getSupportedModes()) {
+ if (Math.round(mode.getRefreshRate()) > maxRefreshRate) {
+ maxRefreshRate = mode.getRefreshRate();
+ }
+ }
+ return maxRefreshRate;
+ }
+
+ /**
+ * Get the min refresh rate which is determined by
+ * {@link Settings.System.FORCE_PEAK_REFRESH_RATE}.
+ * @param context The context
+ * @return The min refresh rate
+ */
+ public static float getMinRefreshRate(Context context) {
+ final ContentResolver cr = context.getContentResolver();
+ int forcePeakRefreshRateSetting = Settings.System.getIntForUser(cr,
+ Settings.System.FORCE_PEAK_REFRESH_RATE, -1, cr.getUserId());
+ return forcePeakRefreshRateSetting == 1
+ ? findHighestRefreshRateForDefaultDisplay(context)
+ : 0;
+ }
+
+ /**
+ * Get the peak refresh rate which is determined by {@link Settings.System.SMOOTH_DISPLAY}.
+ * @param context The context
+ * @param defaultPeakRefreshRate The refresh rate to return if the setting doesn't have a value
+ * @return The peak refresh rate
+ */
+ public static float getPeakRefreshRate(Context context, float defaultPeakRefreshRate) {
+ final ContentResolver cr = context.getContentResolver();
+ int smoothDisplaySetting = Settings.System.getIntForUser(cr,
+ Settings.System.SMOOTH_DISPLAY, -1, cr.getUserId());
+ switch (smoothDisplaySetting) {
+ case 0:
+ return DEFAULT_REFRESH_RATE;
+ case 1:
+ return findHighestRefreshRateForDefaultDisplay(context);
+ default:
+ return defaultPeakRefreshRate;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 0a0785e16f2a..80f540cca79b 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -36,6 +36,7 @@ import android.graphics.HardwareRendererObserver;
import android.os.Handler;
import android.os.Trace;
import android.text.TextUtils;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.view.Choreographer;
@@ -43,7 +44,9 @@ import android.view.FrameMetrics;
import android.view.SurfaceControl;
import android.view.SurfaceControl.JankData.JankType;
import android.view.ThreadedRenderer;
+import android.view.View;
import android.view.ViewRootImpl;
+import android.view.WindowCallbacks;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
@@ -686,6 +689,14 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
}
}
+ ThreadedRendererWrapper getThreadedRenderer() {
+ return mRendererWrapper;
+ }
+
+ ViewRootWrapper getViewRoot() {
+ return mViewRoot;
+ }
+
private boolean shouldTriggerPerfetto(int missedFramesCount, int maxFrameTimeNanos) {
boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1
&& missedFramesCount >= mTraceThresholdMissedFrames;
@@ -798,6 +809,28 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener
public SurfaceControl getSurfaceControl() {
return mViewRoot.getSurfaceControl();
}
+
+ void requestInvalidateRootRenderNode() {
+ mViewRoot.requestInvalidateRootRenderNode();
+ }
+
+ void addWindowCallbacks(WindowCallbacks windowCallbacks) {
+ mViewRoot.addWindowCallbacks(windowCallbacks);
+ }
+
+ void removeWindowCallbacks(WindowCallbacks windowCallbacks) {
+ mViewRoot.removeWindowCallbacks(windowCallbacks);
+ }
+
+ View getView() {
+ return mViewRoot.getView();
+ }
+
+ int dipToPx(int dip) {
+ final DisplayMetrics displayMetrics =
+ mViewRoot.mContext.getResources().getDisplayMetrics();
+ return (int) (displayMetrics.density * dip + 0.5f);
+ }
}
public static class SurfaceControlWrapper {
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 4b9e77e83166..c769fb95e018 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -97,6 +97,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
import android.Manifest;
+import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -104,6 +105,7 @@ import android.annotation.UiThread;
import android.annotation.WorkerThread;
import android.app.ActivityThread;
import android.content.Context;
+import android.graphics.Color;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -143,6 +145,14 @@ import java.util.concurrent.TimeUnit;
* adb shell device_config put interaction_jank_monitor enabled true
* adb shell device_config put interaction_jank_monitor sampling_interval 1
*
+ * On debuggable builds, an overlay can be used to display the name of the
+ * currently running cuj using:
+ *
+ * adb shell device_config put interaction_jank_monitor debug_overlay_enabled true
+ *
+ * NOTE: The overlay will interfere with metrics, so it should only be used
+ * for understanding which UI events correspeond to which CUJs.
+ *
* @hide
*/
public class InteractionJankMonitor {
@@ -159,6 +169,7 @@ public class InteractionJankMonitor {
"trace_threshold_missed_frames";
private static final String SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY =
"trace_threshold_frame_time_millis";
+ private static final String SETTINGS_DEBUG_OVERLAY_ENABLED_KEY = "debug_overlay_enabled";
/** Default to being enabled on debug builds. */
private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE;
/** Default to collecting data for all CUJs. */
@@ -166,6 +177,7 @@ public class InteractionJankMonitor {
/** Default to triggering trace if 3 frames are missed OR a frame takes at least 64ms */
private static final int DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES = 3;
private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
+ private static final boolean DEFAULT_DEBUG_OVERLAY_ENABLED = false;
@VisibleForTesting
public static final int MAX_LENGTH_OF_CUJ_NAME = 80;
@@ -343,6 +355,9 @@ public class InteractionJankMonitor {
private final HandlerThread mWorker;
private final DisplayResolutionTracker mDisplayResolutionTracker;
private final Object mLock = new Object();
+ private @ColorInt int mDebugBgColor = Color.CYAN;
+ private double mDebugYOffset = 0.1;
+ private InteractionMonitorDebugOverlay mDebugOverlay;
private volatile boolean mEnabled = DEFAULT_ENABLED;
private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
@@ -533,7 +548,7 @@ public class InteractionJankMonitor {
if (needRemoveTasks(action, session)) {
getTracker(session.getCuj()).getHandler().runWithScissors(() -> {
removeTimeout(session.getCuj());
- removeTracker(session.getCuj());
+ removeTracker(session.getCuj(), session.getReason());
}, EXECUTOR_TASK_TIMEOUT);
}
}
@@ -699,7 +714,7 @@ public class InteractionJankMonitor {
if (tracker == null) return false;
// if the end call doesn't return true, another thread is handling end of the cuj.
if (tracker.end(REASON_END_NORMAL)) {
- removeTracker(cujType);
+ removeTracker(cujType, REASON_END_NORMAL);
}
return true;
}
@@ -750,7 +765,7 @@ public class InteractionJankMonitor {
if (tracker == null) return false;
// if the cancel call doesn't return true, another thread is handling cancel of the cuj.
if (tracker.cancel(reason)) {
- removeTracker(cujType);
+ removeTracker(cujType, reason);
}
return true;
}
@@ -758,6 +773,13 @@ public class InteractionJankMonitor {
private void putTracker(@CujType int cuj, @NonNull FrameTracker tracker) {
synchronized (mLock) {
mRunningTrackers.put(cuj, tracker);
+ if (mDebugOverlay != null) {
+ mDebugOverlay.onTrackerAdded(cuj, tracker.getViewRoot());
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Added tracker for " + getNameOfCuj(cuj)
+ + ". mRunningTrackers=" + listNamesOfCujs(mRunningTrackers));
+ }
}
}
@@ -767,9 +789,16 @@ public class InteractionJankMonitor {
}
}
- private void removeTracker(@CujType int cuj) {
+ private void removeTracker(@CujType int cuj, int reason) {
synchronized (mLock) {
mRunningTrackers.remove(cuj);
+ if (mDebugOverlay != null) {
+ mDebugOverlay.onTrackerRemoved(cuj, reason, mRunningTrackers);
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Removed tracker for " + getNameOfCuj(cuj)
+ + ". mRunningTrackers=" + listNamesOfCujs(mRunningTrackers));
+ }
}
}
@@ -782,6 +811,16 @@ public class InteractionJankMonitor {
mTraceThresholdFrameTimeMillis = properties.getInt(
SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY,
DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS);
+ // Never allow the debug overlay to be used on user builds
+ boolean debugOverlayEnabled = Build.IS_DEBUGGABLE && properties.getBoolean(
+ SETTINGS_DEBUG_OVERLAY_ENABLED_KEY,
+ DEFAULT_DEBUG_OVERLAY_ENABLED);
+ if (debugOverlayEnabled && mDebugOverlay == null) {
+ mDebugOverlay = new InteractionMonitorDebugOverlay(mDebugBgColor, mDebugYOffset);
+ } else if (!debugOverlayEnabled && mDebugOverlay != null) {
+ mDebugOverlay.dispose();
+ mDebugOverlay = null;
+ }
// The memory visibility is powered by the volatile field, mEnabled.
mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
}
@@ -822,6 +861,39 @@ public class InteractionJankMonitor {
}
/**
+ * Configures the debug overlay used for displaying interaction names on the screen while they
+ * occur.
+ *
+ * @param bgColor the background color of the box used to display the CUJ names
+ * @param yOffset number between 0 and 1 to indicate where the top of the box should be relative
+ * to the height of the screen
+ */
+ public void configDebugOverlay(@ColorInt int bgColor, double yOffset) {
+ mDebugBgColor = bgColor;
+ mDebugYOffset = yOffset;
+ }
+
+ /**
+ * A helper method for getting a string representation of all running CUJs. For example,
+ * "(LOCKSCREEN_TRANSITION_FROM_AOD, IME_INSETS_ANIMATION)"
+ */
+ private static String listNamesOfCujs(SparseArray<FrameTracker> trackers) {
+ if (!DEBUG) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append('(');
+ for (int i = 0; i < trackers.size(); i++) {
+ sb.append(getNameOfCuj(trackers.keyAt(i)));
+ if (i < trackers.size() - 1) {
+ sb.append(", ");
+ }
+ }
+ sb.append(')');
+ return sb.toString();
+ }
+
+ /**
* A helper method to translate CUJ type to CUJ name.
*
* @param cujType the cuj type defined in this file
diff --git a/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java b/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
new file mode 100644
index 000000000000..99b9f2f35fd4
--- /dev/null
+++ b/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
@@ -0,0 +1,240 @@
+/*
+ * 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.internal.jank;
+
+import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
+
+import android.annotation.ColorInt;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.Rect;
+import android.os.Trace;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.WindowCallbacks;
+
+import com.android.internal.jank.FrameTracker.Reasons;
+import com.android.internal.jank.InteractionJankMonitor.CujType;
+
+/**
+ * An overlay that uses WindowCallbacks to draw the names of all running CUJs to the window
+ * associated with one of the CUJs being tracked. There's no guarantee which window it will
+ * draw to. NOTE: sometimes the CUJ names will remain displayed on the screen longer than they
+ * are actually running.
+ * <p>
+ * CUJ names will be drawn as follows:
+ * <ul>
+ * <li> Normal text indicates the CUJ is currently running
+ * <li> Grey text indicates the CUJ ended normally and is no longer running
+ * <li> Red text with a strikethrough indicates the CUJ was canceled or ended abnormally
+ * </ul>
+ */
+class InteractionMonitorDebugOverlay implements WindowCallbacks {
+ private static final int REASON_STILL_RUNNING = -1000;
+ // Sparse array where the key in the CUJ and the value is the session status, or null if
+ // it's currently running
+ private final SparseIntArray mRunningCujs = new SparseIntArray();
+ private FrameTracker.ViewRootWrapper mViewRoot = null;
+ private final Paint mDebugPaint;
+ private final Paint.FontMetrics mDebugFontMetrics;
+ // Used to display the overlay in a different color and position for different processes.
+ // Otherwise, two overlays will overlap and be difficult to read.
+ private final int mBgColor;
+ private final double mYOffset;
+ private final String mPackageName;
+
+ InteractionMonitorDebugOverlay(@ColorInt int bgColor, double yOffset) {
+ mBgColor = bgColor;
+ mYOffset = yOffset;
+ mDebugPaint = new Paint();
+ mDebugPaint.setAntiAlias(false);
+ mDebugFontMetrics = new Paint.FontMetrics();
+ final Context context = ActivityThread.currentApplication();
+ mPackageName = context.getPackageName();
+ }
+
+ void dispose() {
+ if (mViewRoot != null) {
+ mViewRoot.removeWindowCallbacks(this);
+ forceRedraw();
+ }
+ mViewRoot = null;
+ }
+
+ private boolean attachViewRootIfNeeded(FrameTracker.ViewRootWrapper viewRoot) {
+ if (mViewRoot == null && viewRoot != null) {
+ mViewRoot = viewRoot;
+ viewRoot.addWindowCallbacks(this);
+ forceRedraw();
+ return true;
+ }
+ return false;
+ }
+
+ private float getWidthOfLongestCujName(int cujFontSize) {
+ mDebugPaint.setTextSize(cujFontSize);
+ float maxLength = 0;
+ for (int i = 0; i < mRunningCujs.size(); i++) {
+ String cujName = InteractionJankMonitor.getNameOfCuj(mRunningCujs.keyAt(i));
+ float textLength = mDebugPaint.measureText(cujName);
+ if (textLength > maxLength) {
+ maxLength = textLength;
+ }
+ }
+ return maxLength;
+ }
+
+ private float getTextHeight(int textSize) {
+ mDebugPaint.setTextSize(textSize);
+ mDebugPaint.getFontMetrics(mDebugFontMetrics);
+ return mDebugFontMetrics.descent - mDebugFontMetrics.ascent;
+ }
+
+ private int dipToPx(int dip) {
+ if (mViewRoot != null) {
+ return mViewRoot.dipToPx(dip);
+ } else {
+ return dip;
+ }
+ }
+
+ private void forceRedraw() {
+ if (mViewRoot != null) {
+ mViewRoot.requestInvalidateRootRenderNode();
+ mViewRoot.getView().invalidate();
+ }
+ }
+
+ void onTrackerRemoved(@CujType int removedCuj, @Reasons int reason,
+ SparseArray<FrameTracker> runningTrackers) {
+ mRunningCujs.put(removedCuj, reason);
+ // If REASON_STILL_RUNNING is not in mRunningCujs, then all CUJs have ended
+ if (mRunningCujs.indexOfValue(REASON_STILL_RUNNING) < 0) {
+ mRunningCujs.clear();
+ dispose();
+ } else {
+ boolean needsNewViewRoot = true;
+ if (mViewRoot != null) {
+ // Check to see if this viewroot is still associated with one of the running
+ // trackers
+ for (int i = 0; i < runningTrackers.size(); i++) {
+ if (mViewRoot.equals(
+ runningTrackers.valueAt(i).getViewRoot())) {
+ needsNewViewRoot = false;
+ break;
+ }
+ }
+ }
+ if (needsNewViewRoot) {
+ dispose();
+ for (int i = 0; i < runningTrackers.size(); i++) {
+ if (attachViewRootIfNeeded(runningTrackers.valueAt(i).getViewRoot())) {
+ break;
+ }
+ }
+ } else {
+ forceRedraw();
+ }
+ }
+ }
+
+ void onTrackerAdded(@CujType int addedCuj, FrameTracker.ViewRootWrapper viewRoot) {
+ // Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ
+ // is still running
+ mRunningCujs.put(addedCuj, REASON_STILL_RUNNING);
+ attachViewRootIfNeeded(viewRoot);
+ forceRedraw();
+ }
+
+ @Override
+ public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen,
+ Rect systemInsets, Rect stableInsets) {
+ }
+
+ @Override
+ public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen,
+ Rect systemInsets, Rect stableInsets) {
+ }
+
+ @Override
+ public void onWindowDragResizeEnd() {
+ }
+
+ @Override
+ public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
+ return false;
+ }
+
+ @Override
+ public void onRequestDraw(boolean reportNextDraw) {
+ }
+
+ @Override
+ public void onPostDraw(RecordingCanvas canvas) {
+ Trace.beginSection("InteractionJankMonitor#drawDebug");
+ final int padding = dipToPx(5);
+ final int h = canvas.getHeight();
+ final int w = canvas.getWidth();
+ // Draw sysui CUjs near the bottom of the screen so they don't overlap with the shade,
+ // and draw launcher CUJs near the top of the screen so they don't overlap with gestures
+ final int dy = (int) (h * mYOffset);
+ int packageNameFontSize = dipToPx(12);
+ int cujFontSize = dipToPx(18);
+ final float cujNameTextHeight = getTextHeight(cujFontSize);
+ final float packageNameTextHeight = getTextHeight(packageNameFontSize);
+ float maxLength = getWidthOfLongestCujName(cujFontSize);
+
+ final int dx = (int) ((w - maxLength) / 2f);
+ canvas.translate(dx, dy);
+ // Draw background rectangle for displaying the text showing the CUJ name
+ mDebugPaint.setColor(mBgColor);
+ canvas.drawRect(
+ -padding * 2, // more padding on top so we can draw the package name
+ -padding,
+ padding * 2 + maxLength,
+ padding * 2 + packageNameTextHeight + cujNameTextHeight * mRunningCujs.size(),
+ mDebugPaint);
+ mDebugPaint.setTextSize(packageNameFontSize);
+ mDebugPaint.setColor(Color.BLACK);
+ mDebugPaint.setStrikeThruText(false);
+ canvas.translate(0, packageNameTextHeight);
+ canvas.drawText("package:" + mPackageName, 0, 0, mDebugPaint);
+ mDebugPaint.setTextSize(cujFontSize);
+ // Draw text for CUJ names
+ for (int i = 0; i < mRunningCujs.size(); i++) {
+ int status = mRunningCujs.valueAt(i);
+ if (status == REASON_STILL_RUNNING) {
+ mDebugPaint.setColor(Color.BLACK);
+ mDebugPaint.setStrikeThruText(false);
+ } else if (status == REASON_END_NORMAL) {
+ mDebugPaint.setColor(Color.GRAY);
+ mDebugPaint.setStrikeThruText(false);
+ } else {
+ // Cancelled, or otherwise ended for a bad reason
+ mDebugPaint.setColor(Color.RED);
+ mDebugPaint.setStrikeThruText(true);
+ }
+ String cujName = InteractionJankMonitor.getNameOfCuj(mRunningCujs.keyAt(i));
+ canvas.translate(0, cujNameTextHeight);
+ canvas.drawText(cujName, 0, 0, mDebugPaint);
+ }
+ Trace.endSection();
+ }
+}
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 55aa7117221e..4474d4cabacb 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -52,7 +52,7 @@ static JNIEnv* getenv(JavaVM* vm) {
return env;
}
- struct {
+struct {
jmethodID onTransactionHang;
} gTransactionHangCallback;
@@ -72,12 +72,14 @@ public:
}
void onTransactionHang(const std::string& reason) {
- if (mTransactionHangObject) {
- JNIEnv* env = getenv(mVm);
- ScopedLocalRef<jstring> jReason(env, env->NewStringUTF(reason.c_str()));
- getenv(mVm)->CallVoidMethod(mTransactionHangObject,
- gTransactionHangCallback.onTransactionHang, jReason.get());
+ if (!mTransactionHangObject) {
+ return;
}
+ JNIEnv* env = getenv(mVm);
+ ScopedLocalRef<jstring> jReason(env, env->NewStringUTF(reason.c_str()));
+ getenv(mVm)->CallVoidMethod(mTransactionHangObject,
+ gTransactionHangCallback.onTransactionHang, jReason.get());
+ DieIfException(env, "Uncaught exception in TransactionHangCallback.");
}
private:
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index 78e2d3164993..d9152d61ed8a 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -122,6 +122,10 @@ void hintActivityLaunch_native(JNIEnv* env, jobject clazz) {
android::GraphicsEnv::getInstance().hintActivityLaunch();
}
+void nativeToggleAngleAsSystemDriver_native(JNIEnv* env, jobject clazz, jboolean enabled) {
+ android::GraphicsEnv::getInstance().nativeToggleAngleAsSystemDriver(enabled);
+}
+
const JNINativeMethod g_methods[] = {
{"isDebuggable", "()Z", reinterpret_cast<void*>(isDebuggable_native)},
{"setDriverPathAndSphalLibraries", "(Ljava/lang/String;Ljava/lang/String;)V",
@@ -143,6 +147,8 @@ const JNINativeMethod g_methods[] = {
{"setDebugLayersGLES", "(Ljava/lang/String;)V",
reinterpret_cast<void*>(setDebugLayersGLES_native)},
{"hintActivityLaunch", "()V", reinterpret_cast<void*>(hintActivityLaunch_native)},
+ {"nativeToggleAngleAsSystemDriver", "(Z)V",
+ reinterpret_cast<void*>(nativeToggleAngleAsSystemDriver_native)},
};
const char* const kGraphicsEnvironmentName = "android/os/GraphicsEnvironment";
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index e42c6f107e6d..193099baad48 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -285,6 +285,7 @@ public:
JNIEnv* env = getenv();
env->CallVoidMethod(mTransactionCommittedListenerObject,
gTransactionCommittedListenerClassInfo.onTransactionCommitted);
+ DieIfException(env, "Uncaught exception in TransactionCommittedListener.");
}
static void transactionCallbackThunk(void* context, nsecs_t /*latchTime*/,
@@ -325,6 +326,7 @@ public:
binder::Status onWindowInfosReported() override {
JNIEnv* env = getenv();
env->CallVoidMethod(mListener, gRunnableClassInfo.run);
+ DieIfException(env, "Uncaught exception in WindowInfosReportedListener.");
return binder::Status::ok();
}
@@ -356,6 +358,7 @@ public:
env->CallVoidMethod(mTrustedPresentationCallback,
gTrustedPresentationCallbackClassInfo.onTrustedPresentationChanged,
inTrustedPresentationState);
+ DieIfException(env, "Uncaught exception in TrustedPresentationCallback.");
}
void addCallbackRef(const sp<SurfaceComposerClient::PresentationCallbackRAII>& callbackRef) {
@@ -613,6 +616,12 @@ static void nativeSetBuffer(JNIEnv* env, jclass clazz, jlong transactionObj, jlo
genReleaseCallback(env, releaseCallback));
}
+static void nativeUnsetBuffer(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->unsetBuffer(ctrl);
+}
+
static void nativeSetBufferTransform(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint transform) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2195,6 +2204,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetGeometry },
{"nativeSetBuffer", "(JJLandroid/hardware/HardwareBuffer;JLjava/util/function/Consumer;)V",
(void*)nativeSetBuffer },
+ {"nativeUnsetBuffer", "(JJ)V", (void*)nativeUnsetBuffer },
+
{"nativeSetBufferTransform", "(JJI)V", (void*) nativeSetBufferTransform},
{"nativeSetDataSpace", "(JJI)V",
(void*)nativeSetDataSpace },
diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h
index b85a42529fb6..210dc895d674 100644
--- a/core/jni/core_jni_helpers.h
+++ b/core/jni/core_jni_helpers.h
@@ -134,6 +134,15 @@ static inline JNIEnv* GetOrAttachJNIEnvironment(JavaVM* jvm, jint version = JNI_
return env;
}
+static inline void DieIfException(JNIEnv* env, const char* message) {
+ if (env->ExceptionCheck()) {
+ jnihelp::ExpandableString summary;
+ jnihelp::ExpandableStringInitialize(&summary);
+ jnihelp::GetStackTraceOrSummary(env, nullptr, &summary);
+ LOG_ALWAYS_FATAL("%s\n%s", message, summary.data);
+ }
+}
+
} // namespace android
#endif // CORE_JNI_HELPERS
diff --git a/core/proto/android/server/windowmanagertransitiontrace.proto b/core/proto/android/server/windowmanagertransitiontrace.proto
index 25985ebc551a..c3cbc9102b27 100644
--- a/core/proto/android/server/windowmanagertransitiontrace.proto
+++ b/core/proto/android/server/windowmanagertransitiontrace.proto
@@ -55,12 +55,14 @@ message Transition {
optional int64 finish_time_ns = 6; // consider aborted if not provided
required int32 type = 7;
repeated Target targets = 8;
+ optional int32 flags = 9;
}
message Target {
required int32 mode = 1;
required int32 layer_id = 2;
optional int32 window_id = 3; // Not dumped in always on tracing
+ optional int32 flags = 4;
}
message TransitionState {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f6c9fabb7896..997a0c9bf46d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2542,7 +2542,7 @@
<permission android:name="android.permission.TURN_SCREEN_ON"
android:label="@string/permlab_turnScreenOn"
android:description="@string/permdesc_turnScreenOn"
- android:protectionLevel="normal|appop" />
+ android:protectionLevel="signature|privileged|appop" />
<!-- ==================================================== -->
<!-- Permissions related to changing audio settings -->
@@ -7629,6 +7629,13 @@
<permission android:name="android.permission.LOG_FOREGROUND_RESOURCE_USE"
android:protectionLevel="signature|module" />
+ <!-- @hide Allows the settings app to access GPU service APIs".
+ <p>Not for use by third-party applications.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.ACCESS_GPU_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- @hide Allows an application to get type of any provider uri.
<p>Not for use by third-party applications.
<p>Protection level: signature
@@ -8217,6 +8224,14 @@
</intent-filter>
</service>
+ <service android:name="com.android.server.companion.datatransfer.contextsync.CallMetadataSyncConnectionService"
+ android:permission="android.permission.BIND_CONNECTION_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.telecom.ConnectionService"/>
+ </intent-filter>
+ </service>
+
<provider
android:name="com.android.server.textclassifier.IconsContentProvider"
android:authorities="com.android.textclassifier.icons"
diff --git a/core/res/res/drawable/loading_spinner.xml b/core/res/res/drawable/loading_spinner.xml
new file mode 100644
index 000000000000..49603d857a1a
--- /dev/null
+++ b/core/res/res/drawable/loading_spinner.xml
@@ -0,0 +1,55 @@
+<!-- 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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="230dp" android:width="230dp" android:viewportHeight="230"
+ android:viewportWidth="230">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_0_G" android:translateX="100.621"
+ android:translateY="102.621">
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:strokeColor="#ffffff"
+ android:strokeLineCap="round" android:strokeLineJoin="round"
+ android:strokeWidth="8" android:strokeAlpha="1" android:trimPathStart="0"
+ android:trimPathEnd="0" android:trimPathOffset="0"
+ android:pathData=" M14.38 -93.62 C72.88,-93.62 120.38,-46.12 120.38,12.38 C120.38,70.88 72.88,118.38 14.38,118.38 C-44.12,118.38 -91.62,70.88 -91.62,12.38 C-91.62,-46.12 -44.12,-93.62 14.38,-93.62c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="trimPathEnd" android:duration="350"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.4,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX" android:duration="517"
+ android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector> \ No newline at end of file
diff --git a/core/res/res/layout/user_switching_dialog.xml b/core/res/res/layout/user_switching_dialog.xml
index 2e041f5f2be2..496179aefc91 100644
--- a/core/res/res/layout/user_switching_dialog.xml
+++ b/core/res/res/layout/user_switching_dialog.xml
@@ -14,17 +14,48 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content"
+ android:background="?attr/colorBackground"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/message"
- style="?attr/textAppearanceListItem"
- android:background="?attr/colorSurface"
+ <LinearLayout
android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
android:gravity="center"
- android:drawablePadding="12dp"
- android:drawableTint="?attr/textColorPrimary"
- android:paddingStart="?attr/dialogPreferredPadding"
- android:paddingEnd="?attr/dialogPreferredPadding"
- android:paddingTop="24dp"
- android:paddingBottom="24dp" />
+ android:orientation="vertical"
+ android:paddingBottom="77dp">
+
+ <RelativeLayout
+ android:layout_width="242dp"
+ android:layout_height="242dp"
+ android:layout_gravity="center">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="26dp" />
+
+ <ImageView
+ android:id="@+id/progress_circular"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="6dp"
+ android:src="@drawable/loading_spinner" />
+
+ </RelativeLayout>
+
+ <TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/message"
+ style="?attr/textAppearanceListItem"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="20sp"
+ android:textAlignment="center"
+ android:drawableTint="?attr/textColorPrimary" />
+
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5544701d9325..c5f7ea6501ff 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1917,7 +1917,8 @@
<string name="config_defaultNetworkRecommendationProviderPackage" translatable="false"></string>
<!-- The package name of the default search selector app. Must be granted the POST_NOTIFICATIONS
- permission.
+ permission, and notifications from this app must be given the notification flag
+ FLAG_NO_DISMISS if the notification requests FLAG_ONGOING_EVENT.
-->
<string name="config_defaultSearchSelectorPackageName" translatable="false"></string>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 3ea15924e96d..34b4c5177852 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -91,8 +91,8 @@ android_test {
":BstatsTestApp",
":BinderDeathRecipientHelperApp1",
":BinderDeathRecipientHelperApp2",
+ ":com.android.cts.helpers.aosp",
],
- required: ["com.android.cts.helpers.aosp"],
}
// Rules to copy all the test apks to the intermediate raw resource directory
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 4f91e7a3545a..fb0f3d419002 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -71,6 +71,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.content.ReferrerIntent;
import org.junit.After;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -95,7 +96,8 @@ public class ActivityThreadTest {
// few sequence numbers the framework used to launch the test activity.
private static final int BASE_SEQ = 10000;
- private final ActivityTestRule<TestActivity> mActivityTestRule =
+ @Rule
+ public final ActivityTestRule<TestActivity> mActivityTestRule =
new ActivityTestRule<>(TestActivity.class, true /* initialTouchMode */,
false /* launchActivity */);
diff --git a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
index 1075d44b4017..07dec5d9e222 100644
--- a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
@@ -17,6 +17,7 @@
package android.hardware.face;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_HW_UNAVAILABLE;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS;
import static com.google.common.truth.Truth.assertThat;
@@ -178,6 +179,18 @@ public class FaceManagerTest {
verify(mEnrollmentCallback).onEnrollmentError(eq(FACE_ERROR_HW_UNAVAILABLE), anyString());
}
+ @Test
+ public void enrollment_errorWhenHardwareAuthTokenIsNull() throws RemoteException {
+ initializeProperties();
+ mFaceManager.enroll(USER_ID, null,
+ new CancellationSignal(), mEnrollmentCallback, null /* disabledFeatures */);
+
+ verify(mEnrollmentCallback).onEnrollmentError(eq(FACE_ERROR_UNABLE_TO_PROCESS),
+ anyString());
+ verify(mService, never()).enroll(eq(USER_ID), any(), any(),
+ any(), anyString(), any(), any(), anyBoolean());
+ }
+
private void initializeProperties() throws RemoteException {
verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture());
diff --git a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
index 5058065710be..625e2e3723a7 100644
--- a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
@@ -17,12 +17,14 @@
package android.hardware.fingerprint;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_PROCESS;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -70,6 +72,8 @@ public class FingerprintManagerTest {
private IFingerprintService mService;
@Mock
private FingerprintManager.AuthenticationCallback mAuthCallback;
+ @Mock
+ private FingerprintManager.EnrollmentCallback mEnrollCallback;
@Captor
private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mCaptor;
@@ -149,4 +153,17 @@ public class FingerprintManagerTest {
verify(mAuthCallback).onAuthenticationError(eq(FINGERPRINT_ERROR_HW_UNAVAILABLE), any());
}
+
+ @Test
+ public void enrollment_errorWhenHardwareAuthTokenIsNull() throws RemoteException {
+ verify(mService).addAuthenticatorsRegisteredCallback(mCaptor.capture());
+
+ mCaptor.getValue().onAllAuthenticatorsRegistered(mProps);
+ mFingerprintManager.enroll(null, new CancellationSignal(), USER_ID,
+ mEnrollCallback, FingerprintManager.ENROLL_ENROLL);
+
+ verify(mEnrollCallback).onEnrollmentError(eq(FINGERPRINT_ERROR_UNABLE_TO_PROCESS),
+ anyString());
+ verify(mService, never()).enroll(any(), any(), anyInt(), any(), anyString(), anyInt());
+ }
}
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index 8ae63816ba16..c0125afef2e8 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -23,14 +23,20 @@ import static android.view.stylus.HandwritingTestUtil.createView;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+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;
import android.app.Instrumentation;
import android.content.Context;
+import android.graphics.Canvas;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.HandwritingInitiator;
@@ -38,7 +44,9 @@ import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -47,6 +55,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
/**
* Tests for {@link HandwritingInitiator}
@@ -543,6 +552,111 @@ public class HandwritingInitiatorTest {
assertThat(mHandwritingInitiator.mConnectedView.get()).isEqualTo(mTestView1);
}
+ @Test
+ public void startHandwriting_hidesHint() {
+ EditText editText =
+ new EditText(InstrumentationRegistry.getInstrumentation().getTargetContext());
+ editText.setHint("hint");
+ editText.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+
+ verifyEditTextDrawsText(editText, "hint");
+
+ mHandwritingInitiator.onTouchEvent(createStylusEvent(ACTION_DOWN, 0, 0, 0));
+ mHandwritingInitiator.startHandwriting(editText);
+
+ verifyEditTextDrawsText(editText, null);
+ }
+
+ @Test
+ public void startHandwriting_clearFocus_restoresHint() {
+ EditText editText =
+ new EditText(InstrumentationRegistry.getInstrumentation().getTargetContext());
+ editText.setHint("hint");
+ editText.setLayoutParams(new ViewGroup.LayoutParams(1024, 1024));
+ editText.requestFocus();
+
+ verifyEditTextDrawsText(editText, "hint");
+
+ mHandwritingInitiator.onTouchEvent(createStylusEvent(ACTION_DOWN, 0, 0, 0));
+ mHandwritingInitiator.startHandwriting(editText);
+
+ verifyEditTextDrawsText(editText, null);
+
+ editText.clearFocus();
+
+ verifyEditTextDrawsText(editText, "hint");
+ }
+
+ @Test
+ public void startHandwriting_setHint_restoresHint() {
+ EditText editText =
+ new EditText(InstrumentationRegistry.getInstrumentation().getTargetContext());
+ editText.setHint("hint");
+ editText.setLayoutParams(new ViewGroup.LayoutParams(1024, 1024));
+
+ verifyEditTextDrawsText(editText, "hint");
+
+ mHandwritingInitiator.onTouchEvent(createStylusEvent(ACTION_DOWN, 0, 0, 0));
+ mHandwritingInitiator.startHandwriting(editText);
+
+ verifyEditTextDrawsText(editText, null);
+
+ editText.setHint("new hint");
+
+ verifyEditTextDrawsText(editText, "new hint");
+ }
+
+ @Test
+ public void startHandwriting_setText_restoresHint() {
+ EditText editText =
+ new EditText(InstrumentationRegistry.getInstrumentation().getTargetContext());
+ editText.setHint("hint");
+ editText.setLayoutParams(new ViewGroup.LayoutParams(1024, 1024));
+
+ verifyEditTextDrawsText(editText, "hint");
+
+ mHandwritingInitiator.onTouchEvent(createStylusEvent(ACTION_DOWN, 0, 0, 0));
+ mHandwritingInitiator.startHandwriting(editText);
+
+ verifyEditTextDrawsText(editText, null);
+
+ editText.setText("a");
+ editText.setText("");
+
+ verifyEditTextDrawsText(editText, "hint");
+ }
+
+ private void verifyEditTextDrawsText(EditText editText, String text) {
+ editText.measure(
+ View.MeasureSpec.makeMeasureSpec(1024, View.MeasureSpec.AT_MOST),
+ View.MeasureSpec.makeMeasureSpec(1024, View.MeasureSpec.AT_MOST));
+ Canvas canvas = prepareMockCanvas(editText);
+ editText.draw(canvas);
+ if (text != null) {
+ ArgumentCaptor<CharSequence> textCaptor = ArgumentCaptor.forClass(CharSequence.class);
+ verify(canvas).drawText(
+ textCaptor.capture(), anyInt(), anyInt(), anyFloat(), anyFloat(), any());
+ assertThat(textCaptor.getValue().toString()).isEqualTo(text);
+ } else {
+ verify(canvas, never()).drawText(
+ any(CharSequence.class), anyInt(), anyInt(), anyFloat(), anyFloat(), any());
+ }
+ }
+
+ private Canvas prepareMockCanvas(View view) {
+ Canvas canvas = mock(Canvas.class);
+ when(canvas.getClipBounds(any())).thenAnswer(invocation -> {
+ Rect outRect = invocation.getArgument(0);
+ outRect.top = 0;
+ outRect.left = 0;
+ outRect.right = view.getMeasuredWidth();
+ outRect.bottom = view.getMeasuredHeight();
+ return true;
+ });
+ return canvas;
+ }
+
private MotionEvent createStylusEvent(int action, int x, int y, long eventTime) {
MotionEvent.PointerProperties[] properties = MotionEvent.PointerProperties.createArray(1);
properties[0].toolType = MotionEvent.TOOL_TYPE_STYLUS;
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
index 03d366e6e552..57a1376a6cd1 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutChooserActivityTest.java
@@ -46,6 +46,10 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Handler;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
@@ -57,6 +61,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.internal.accessibility.dialog.AccessibilityShortcutChooserActivity;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -73,8 +78,14 @@ import java.util.Collections;
@RunWith(AndroidJUnit4.class)
public class AccessibilityShortcutChooserActivityTest {
private static final String ONE_HANDED_MODE = "One-Handed mode";
+ private static final String DENY_LABEL = "Deny";
+ private static final String EDIT_LABEL = "Edit shortcuts";
+ private static final String LIST_TITLE_LABEL = "Choose features to use";
private static final String TEST_LABEL = "TEST_LABEL";
private static final ComponentName TEST_COMPONENT_NAME = new ComponentName("package", "class");
+ private static final long UI_TIMEOUT_MS = 1000;
+ private UiDevice mDevice;
+ private ActivityScenario<TestAccessibilityShortcutChooserActivity> mScenario;
private TestAccessibilityShortcutChooserActivity mActivity;
@Rule
@@ -92,6 +103,8 @@ public class AccessibilityShortcutChooserActivityTest {
@Before
public void setUp() throws Exception {
+ mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ mDevice.wakeUp();
when(mAccessibilityServiceInfo.getResolveInfo()).thenReturn(mResolveInfo);
mResolveInfo.serviceInfo = mServiceInfo;
mServiceInfo.applicationInfo = mApplicationInfo;
@@ -102,27 +115,36 @@ public class AccessibilityShortcutChooserActivityTest {
when(mAccessibilityManagerService.isAccessibilityTargetAllowed(
anyString(), anyInt(), anyInt())).thenReturn(true);
TestAccessibilityShortcutChooserActivity.setupForTesting(mAccessibilityManagerService);
+ mScenario = ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
+ mScenario.onActivity(activity -> mActivity = activity);
+ mScenario.moveToState(Lifecycle.State.CREATED);
+ mScenario.moveToState(Lifecycle.State.STARTED);
+ mScenario.moveToState(Lifecycle.State.RESUMED);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @After
+ public void cleanUp() {
+ mScenario.moveToState(Lifecycle.State.DESTROYED);
}
@Test
public void doubleClickTestServiceAndClickDenyButton_permissionDialogDoesNotExist() {
- final ActivityScenario<TestAccessibilityShortcutChooserActivity> scenario =
- ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
- scenario.moveToState(Lifecycle.State.CREATED);
- scenario.moveToState(Lifecycle.State.STARTED);
- scenario.moveToState(Lifecycle.State.RESUMED);
-
- onView(withText(R.string.accessibility_select_shortcut_menu_title)).inRoot(
- isDialog()).check(matches(isDisplayed()));
- onView(withText(R.string.edit_accessibility_shortcut_menu_button)).perform(click());
- onView(withText(TEST_LABEL)).perform(scrollTo(), doubleClick());
- onView(withId(R.id.accessibility_permission_enable_deny_button)).perform(scrollTo(),
- click());
+ openShortcutsList();
+
+ // Performing the double-click is flaky so retry if needed.
+ for (int attempt = 1; attempt <= 2; attempt++) {
+ onView(withText(TEST_LABEL)).perform(scrollTo(), doubleClick());
+ if (mDevice.wait(Until.hasObject(By.text(DENY_LABEL)), UI_TIMEOUT_MS)) {
+ break;
+ }
+ }
+
+ onView(withText(DENY_LABEL)).perform(scrollTo(), click());
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
onView(withId(R.id.accessibility_permissionDialog_title)).inRoot(isDialog()).check(
doesNotExist());
- scenario.moveToState(Lifecycle.State.DESTROYED);
}
@Test
@@ -130,60 +152,42 @@ public class AccessibilityShortcutChooserActivityTest {
throws Exception {
when(mAccessibilityManagerService.isAccessibilityTargetAllowed(
eq(TEST_COMPONENT_NAME.getPackageName()), anyInt(), anyInt())).thenReturn(false);
- final ActivityScenario<TestAccessibilityShortcutChooserActivity> scenario =
- ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
- scenario.onActivity(activity -> mActivity = activity);
- scenario.moveToState(Lifecycle.State.CREATED);
- scenario.moveToState(Lifecycle.State.STARTED);
- scenario.moveToState(Lifecycle.State.RESUMED);
-
- onView(withText(R.string.accessibility_select_shortcut_menu_title)).inRoot(
- isDialog()).check(matches(isDisplayed()));
- onView(withText(R.string.edit_accessibility_shortcut_menu_button)).perform(click());
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ openShortcutsList();
onView(withText(TEST_LABEL)).perform(scrollTo(), click());
+
verify(mAccessibilityManagerService).sendRestrictedDialogIntent(
eq(TEST_COMPONENT_NAME.getPackageName()), anyInt(), anyInt());
- scenario.moveToState(Lifecycle.State.DESTROYED);
}
@Test
public void popEditShortcutMenuList_oneHandedModeEnabled_shouldBeInListView() {
TestUtils.setOneHandedModeEnabled(this, /* enabled= */ true);
- final ActivityScenario<TestAccessibilityShortcutChooserActivity> scenario =
- ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
- scenario.moveToState(Lifecycle.State.CREATED);
- scenario.moveToState(Lifecycle.State.STARTED);
- scenario.moveToState(Lifecycle.State.RESUMED);
-
- onView(withText(R.string.accessibility_select_shortcut_menu_title)).inRoot(
- isDialog()).check(matches(isDisplayed()));
- onView(withText(R.string.edit_accessibility_shortcut_menu_button)).perform(click());
+ openShortcutsList();
+
onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp());
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ mDevice.wait(Until.hasObject(By.text(ONE_HANDED_MODE)), UI_TIMEOUT_MS);
onView(withText(ONE_HANDED_MODE)).inRoot(isDialog()).check(matches(isDisplayed()));
- scenario.moveToState(Lifecycle.State.DESTROYED);
}
@Test
public void popEditShortcutMenuList_oneHandedModeDisabled_shouldNotBeInListView() {
TestUtils.setOneHandedModeEnabled(this, /* enabled= */ false);
- final ActivityScenario<TestAccessibilityShortcutChooserActivity> scenario =
- ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class);
- scenario.moveToState(Lifecycle.State.CREATED);
- scenario.moveToState(Lifecycle.State.STARTED);
- scenario.moveToState(Lifecycle.State.RESUMED);
-
- onView(withText(R.string.accessibility_select_shortcut_menu_title)).inRoot(
- isDialog()).check(matches(isDisplayed()));
- onView(withText(R.string.edit_accessibility_shortcut_menu_button)).perform(click());
+ openShortcutsList();
+
onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp());
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
onView(withText(ONE_HANDED_MODE)).inRoot(isDialog()).check(doesNotExist());
- scenario.moveToState(Lifecycle.State.DESTROYED);
+ }
+
+ private void openShortcutsList() {
+ UiObject2 editButton = mDevice.findObject(By.text(EDIT_LABEL));
+ if (editButton != null) {
+ editButton.click();
+ }
+ mDevice.wait(Until.hasObject(By.textStartsWith(LIST_TITLE_LABEL)), UI_TIMEOUT_MS);
}
/**
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTest.java b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
index 4a12bb374cb2..5e617fd59946 100644
--- a/core/tests/mockingcoretests/src/android/view/DisplayTest.java
+++ b/core/tests/mockingcoretests/src/android/view/DisplayTest.java
@@ -460,6 +460,36 @@ public class DisplayTest {
assertArrayEquals(sortedHdrTypes, displayMode.getSupportedHdrTypes());
}
+ @Test
+ public void testGetSupportedHdrTypesReturnsCopy() {
+ int[] hdrTypes = new int[]{1, 2, 3};
+ Display.Mode displayMode = new Display.Mode(0, 0, 0, 0, new float[0], hdrTypes);
+
+ int[] hdrTypesCopy = displayMode.getSupportedHdrTypes();
+ hdrTypesCopy[0] = 0;
+ assertArrayEquals(hdrTypes, displayMode.getSupportedHdrTypes());
+ }
+
+ @Test
+ public void testGetAlternativeRefreshRatesReturnsCopy() {
+ float[] alternativeRates = new float[]{1.0f, 2.0f};
+ Display.Mode displayMode = new Display.Mode(0, 0, 0, 0, alternativeRates, new int[0]);
+
+ float[] alternativeRatesCopy = displayMode.getAlternativeRefreshRates();
+ alternativeRatesCopy[0] = 0.0f;
+ assertArrayEquals(alternativeRates, displayMode.getAlternativeRefreshRates(), 0.0f);
+ }
+
+ @Test
+ public void testHdrCapabilitiesGetSupportedHdrTypesReturnsCopy() {
+ int[] hdrTypes = new int[]{1, 2, 3};
+ Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(hdrTypes, 0, 0, 0);
+
+ int[] hdrTypesCopy = hdrCapabilities.getSupportedHdrTypes();
+ hdrTypesCopy[0] = 0;
+ assertArrayEquals(hdrTypes, hdrCapabilities.getSupportedHdrTypes());
+ }
+
// Given rotated display dimensions, calculate the letterboxed app bounds.
private static Rect buildAppBounds(int displayWidth, int displayHeight) {
final int midWidth = displayWidth / 2;
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 7434cb02cc7c..549ac5858d1d 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -3805,6 +3805,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
+ "1463355909": {
+ "message": "Queueing legacy sync-set: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN",
+ "at": "com\/android\/server\/wm\/TransitionController.java"
+ },
"1469310004": {
"message": " SKIP: common mode mismatch. was %s",
"level": "VERBOSE",
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 56c3068fe5e9..302c72ead52e 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -42,7 +42,6 @@ import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.net.Uri;
import android.os.Build;
-import android.os.SystemProperties;
import android.os.Trace;
import android.system.ErrnoException;
import android.system.Os;
@@ -2069,47 +2068,67 @@ public final class ImageDecoder implements AutoCloseable {
}
private static boolean sIsP010SupportedForAV1 = false;
- private static boolean sIsP010SupportedForAV1Initialized = false;
- private static final Object sIsP010SupportedForAV1Lock = new Object();
+ private static boolean sIsP010SupportedForHEVC = false;
+ private static boolean sIsP010SupportedFlagsInitialized = false;
+ private static final Object sIsP010SupportedLock = new Object();
/**
* Checks if the device supports decoding 10-bit AV1.
*/
@SuppressWarnings("AndroidFrameworkCompatChange") // This is not an app-visible API.
private static boolean isP010SupportedForAV1() {
- synchronized (sIsP010SupportedForAV1Lock) {
- if (sIsP010SupportedForAV1Initialized) {
+ synchronized (sIsP010SupportedLock) {
+ if (sIsP010SupportedFlagsInitialized) {
return sIsP010SupportedForAV1;
}
+ checkP010SupportforAV1HEVC();
+ return sIsP010SupportedForAV1;
+ }
+ }
- sIsP010SupportedForAV1Initialized = true;
- return sIsP010SupportedForAV1 = isP010SupportedforMime("video/av01");
+ /**
+ * Checks if the device supports decoding 10-bit HEVC.
+ * This method is called by JNI.
+ */
+ @SuppressWarnings("unused")
+ private static boolean isP010SupportedForHEVC() {
+ synchronized (sIsP010SupportedLock) {
+ if (sIsP010SupportedFlagsInitialized) {
+ return sIsP010SupportedForHEVC;
+ }
+ checkP010SupportforAV1HEVC();
+ return sIsP010SupportedForHEVC;
}
}
/**
* Checks if the device supports decoding 10-bit for the given mime type.
*/
- private static boolean isP010SupportedforMime(String mime) {
+ private static void checkP010SupportforAV1HEVC() {
MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
for (MediaCodecInfo mediaCodecInfo : codecList.getCodecInfos()) {
if (mediaCodecInfo.isEncoder()) {
continue;
}
for (String mediaType : mediaCodecInfo.getSupportedTypes()) {
- if (mediaType.equalsIgnoreCase(mime)) {
+ if (mediaType.equalsIgnoreCase("video/av01")
+ || mediaType.equalsIgnoreCase("video/hevc")) {
MediaCodecInfo.CodecCapabilities codecCapabilities =
mediaCodecInfo.getCapabilitiesForType(mediaType);
for (int i = 0; i < codecCapabilities.colorFormats.length; ++i) {
if (codecCapabilities.colorFormats[i]
== MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010) {
- return true;
+ if (mediaType.equalsIgnoreCase("video/av01")) {
+ sIsP010SupportedForAV1 = true;
+ } else {
+ sIsP010SupportedForHEVC = true;
+ }
}
}
}
}
}
- return false;
+ sIsP010SupportedFlagsInitialized = true;
}
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index 66f27f517ab3..a184dff5005b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -39,7 +39,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
import java.util.function.Consumer;
/**
@@ -167,14 +166,13 @@ public final class DeviceStateManagerFoldingFeatureProducer
}
@Override
- protected void onListenersChanged(
- @NonNull Set<Consumer<List<CommonFoldingFeature>>> callbacks) {
- super.onListenersChanged(callbacks);
- if (callbacks.isEmpty()) {
+ protected void onListenersChanged() {
+ super.onListenersChanged();
+ if (hasListeners()) {
+ mRawFoldSupplier.addDataChangedCallback(this::notifyFoldingFeatureChange);
+ } else {
mCurrentDeviceState = INVALID_DEVICE_STATE;
mRawFoldSupplier.removeDataChangedCallback(this::notifyFoldingFeatureChange);
- } else {
- mRawFoldSupplier.addDataChangedCallback(this::notifyFoldingFeatureChange);
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java
index 7906342d445d..8906e6d3d02e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java
@@ -31,7 +31,6 @@ import androidx.window.util.BaseDataProducer;
import com.android.internal.R;
import java.util.Optional;
-import java.util.Set;
import java.util.function.Consumer;
/**
@@ -86,11 +85,11 @@ public final class RawFoldingFeatureProducer extends BaseDataProducer<String> {
}
@Override
- protected void onListenersChanged(Set<Consumer<String>> callbacks) {
- if (callbacks.isEmpty()) {
- unregisterObserversIfNeeded();
- } else {
+ protected void onListenersChanged() {
+ if (hasListeners()) {
registerObserversIfNeeded();
+ } else {
+ unregisterObserversIfNeeded();
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentation.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentation.java
index 1ff169433b9d..849b500f36e4 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentation.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/RearDisplayPresentation.java
@@ -42,14 +42,14 @@ class RearDisplayPresentation extends Presentation implements ExtensionWindowAre
/**
* {@code mStateConsumer} is notified that their content is now visible when the
* {@link Presentation} object is started. There is no comparable callback for
- * {@link WindowAreaComponent#SESSION_STATE_INVISIBLE} in {@link #onStop()} due to the
+ * {@link WindowAreaComponent#SESSION_STATE_CONTENT_INVISIBLE} in {@link #onStop()} due to the
* timing of when a {@link android.hardware.devicestate.DeviceStateRequest} is cancelled
* ending rear display presentation mode happening before the {@link Presentation} is stopped.
*/
@Override
protected void onStart() {
super.onStart();
- mStateConsumer.accept(WindowAreaComponent.SESSION_STATE_VISIBLE);
+ mStateConsumer.accept(WindowAreaComponent.SESSION_STATE_CONTENT_VISIBLE);
}
@NonNull
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index cc46a4bc4ea3..abf230117032 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -328,8 +328,8 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
// due to not having a good mechanism to know when
// the content is no longer visible before it's fully removed
if (getLastReportedRearDisplayPresentationStatus()
- == SESSION_STATE_VISIBLE) {
- consumer.accept(SESSION_STATE_INVISIBLE);
+ == SESSION_STATE_CONTENT_VISIBLE) {
+ consumer.accept(SESSION_STATE_CONTENT_INVISIBLE);
}
mRearDisplayPresentationController = null;
}
@@ -414,8 +414,7 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
return WindowAreaComponent.STATUS_UNAVAILABLE;
}
- if (mRearDisplaySessionStatus == WindowAreaComponent.SESSION_STATE_ACTIVE
- || isRearDisplayActive()) {
+ if (isRearDisplayActive()) {
return WindowAreaComponent.STATUS_ACTIVE;
}
@@ -537,7 +536,6 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
if (request.equals(mRearDisplayStateRequest)) {
mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_ACTIVE;
mRearDisplaySessionCallback.accept(mRearDisplaySessionStatus);
- updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus());
}
}
}
@@ -550,7 +548,6 @@ public class WindowAreaComponentImpl implements WindowAreaComponent,
}
mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE;
mRearDisplaySessionCallback.accept(mRearDisplaySessionStatus);
- updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus());
}
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
index 46c925aaf8a2..de52f0969fa8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
@@ -51,10 +51,10 @@ public abstract class BaseDataProducer<T> implements DataProducer<T>,
public final void addDataChangedCallback(@NonNull Consumer<T> callback) {
synchronized (mLock) {
mCallbacks.add(callback);
- Optional<T> currentData = getCurrentData();
- currentData.ifPresent(callback);
- onListenersChanged(mCallbacks);
}
+ Optional<T> currentData = getCurrentData();
+ currentData.ifPresent(callback);
+ onListenersChanged();
}
/**
@@ -67,11 +67,22 @@ public abstract class BaseDataProducer<T> implements DataProducer<T>,
public final void removeDataChangedCallback(@NonNull Consumer<T> callback) {
synchronized (mLock) {
mCallbacks.remove(callback);
- onListenersChanged(mCallbacks);
}
+ onListenersChanged();
}
- protected void onListenersChanged(Set<Consumer<T>> callbacks) {}
+ /**
+ * Returns {@code true} if there are any registered callbacks {@code false} if there are no
+ * registered callbacks.
+ */
+ // TODO(b/278132889) Improve the structure of BaseDataProdcuer while avoiding known issues.
+ public final boolean hasListeners() {
+ synchronized (mLock) {
+ return !mCallbacks.isEmpty();
+ }
+ }
+
+ protected void onListenersChanged() {}
/**
* @return the current data if available and {@code Optional.empty()} otherwise.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 1b20f67e42ab..f8f8897e41d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1068,6 +1068,7 @@ public class BubbleStackView extends FrameLayout
// We need to be Z ordered on top in order for alpha animations to work.
mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(true);
mExpandedBubble.getExpandedView().setAnimating(true);
+ mExpandedViewContainer.setVisibility(VISIBLE);
}
}
@@ -2922,14 +2923,15 @@ public class BubbleStackView extends FrameLayout
final float targetX = isLtr
? mTempRect.left - margin
: mTempRect.right + margin - mManageMenu.getWidth();
- final float targetY = mTempRect.bottom - mManageMenu.getHeight();
+ final float menuHeight = getVisibleManageMenuHeight();
+ final float targetY = mTempRect.bottom - menuHeight;
final float xOffsetForAnimation = (isLtr ? 1 : -1) * mManageMenu.getWidth() / 4f;
if (show) {
mManageMenu.setScaleX(0.5f);
mManageMenu.setScaleY(0.5f);
mManageMenu.setTranslationX(targetX - xOffsetForAnimation);
- mManageMenu.setTranslationY(targetY + mManageMenu.getHeight() / 4f);
+ mManageMenu.setTranslationY(targetY + menuHeight / 4f);
mManageMenu.setAlpha(0f);
PhysicsAnimator.getInstance(mManageMenu)
@@ -2955,7 +2957,7 @@ public class BubbleStackView extends FrameLayout
.spring(DynamicAnimation.SCALE_X, 0.5f)
.spring(DynamicAnimation.SCALE_Y, 0.5f)
.spring(DynamicAnimation.TRANSLATION_X, targetX - xOffsetForAnimation)
- .spring(DynamicAnimation.TRANSLATION_Y, targetY + mManageMenu.getHeight() / 4f)
+ .spring(DynamicAnimation.TRANSLATION_Y, targetY + menuHeight / 4f)
.withEndActions(() -> {
mManageMenu.setVisibility(View.INVISIBLE);
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
@@ -3115,7 +3117,7 @@ public class BubbleStackView extends FrameLayout
mAnimatingOutBubbleBuffer.getColorSpace());
mAnimatingOutSurfaceView.setAlpha(1f);
- mExpandedViewContainer.setVisibility(View.GONE);
+ mExpandedViewContainer.setVisibility(View.INVISIBLE);
mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
post(() -> {
@@ -3145,9 +3147,6 @@ public class BubbleStackView extends FrameLayout
int[] paddings = mPositioner.getExpandedViewContainerPadding(
mStackAnimationController.isStackOnLeftSide(), isOverflowExpanded);
mExpandedViewContainer.setPadding(paddings[0], paddings[1], paddings[2], paddings[3]);
- if (mIsExpansionAnimating) {
- mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
- }
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
PointF p = mPositioner.getExpandedBubbleXY(getBubbleIndex(mExpandedBubble),
getState());
@@ -3272,6 +3271,24 @@ public class BubbleStackView extends FrameLayout
}
/**
+ * Menu height calculated for animation
+ * It takes into account view visibility to get the correct total height
+ */
+ private float getVisibleManageMenuHeight() {
+ float menuHeight = 0;
+
+ for (int i = 0; i < mManageMenu.getChildCount(); i++) {
+ View subview = mManageMenu.getChildAt(i);
+
+ if (subview.getVisibility() == VISIBLE) {
+ menuHeight += subview.getHeight();
+ }
+ }
+
+ return menuHeight;
+ }
+
+ /**
* @return the normalized x-axis position of the bubble stack rounded to 4 decimal places.
*/
public float getNormalizedXPosition() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 170c0ee91b40..659229228a57 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -20,6 +20,7 @@ import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -46,11 +47,6 @@ import java.util.function.Consumer;
*/
class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
- /**
- * The Compat UI should be below the Letterbox Education.
- */
- private static final int Z_ORDER = LetterboxEduWindowManager.Z_ORDER - 1;
-
private final CompatUICallback mCallback;
private final CompatUIConfiguration mCompatUIConfiguration;
@@ -92,7 +88,7 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
@Override
protected int getZOrder() {
- return Z_ORDER;
+ return TASK_CHILD_LAYER_COMPAT_UI + 1;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java
index 0c21c8ccd686..959c50d5c640 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.compatui;
import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
+import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -46,12 +47,6 @@ import java.util.function.Consumer;
*/
class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
- /**
- * The Letterbox Education should be the topmost child of the Task in case there can be more
- * than one child.
- */
- public static final int Z_ORDER = Integer.MAX_VALUE;
-
private final DialogAnimationController<LetterboxEduDialogLayout> mAnimationController;
private final Transitions mTransitions;
@@ -118,7 +113,7 @@ class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
@Override
protected int getZOrder() {
- return Z_ORDER;
+ return TASK_CHILD_LAYER_COMPAT_UI + 2;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
index b6e396d12512..a18ab9154e01 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.compatui;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -41,11 +42,6 @@ import com.android.wm.shell.common.SyncTransactionQueue;
*/
class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
- /**
- * The Compat UI should be below the Letterbox Education.
- */
- private static final int Z_ORDER = LetterboxEduWindowManager.Z_ORDER - 1;
-
// The time to wait before hiding the education
private static final long DISAPPEAR_DELAY_MS = 4000L;
@@ -102,7 +98,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
@Override
protected int getZOrder() {
- return Z_ORDER;
+ return TASK_CHILD_LAYER_COMPAT_UI + 1;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java
index aab123a843ea..51e5141a28af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.compatui;
import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
+import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -47,12 +48,6 @@ import java.util.function.Consumer;
*/
class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
- /**
- * The restart dialog should be the topmost child of the Task in case there can be more
- * than one child.
- */
- private static final int Z_ORDER = Integer.MAX_VALUE;
-
private final DialogAnimationController<RestartDialogLayout> mAnimationController;
private final Transitions mTransitions;
@@ -112,7 +107,7 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
@Override
protected int getZOrder() {
- return Z_ORDER;
+ return TASK_CHILD_LAYER_COMPAT_UI + 2;
}
@Override
@@ -170,10 +165,10 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
final Rect taskBounds = getTaskBounds();
final Rect taskStableBounds = getTaskStableBounds();
-
- marginParams.topMargin = taskStableBounds.top - taskBounds.top + mDialogVerticalMargin;
- marginParams.bottomMargin =
- taskBounds.bottom - taskStableBounds.bottom + mDialogVerticalMargin;
+ // only update margins based on taskbar insets
+ marginParams.topMargin = mDialogVerticalMargin;
+ marginParams.bottomMargin = taskBounds.bottom - taskStableBounds.bottom
+ + mDialogVerticalMargin;
dialogContainer.setLayoutParams(marginParams);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
index cbd544cc4b86..e732a0354806 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
@@ -16,9 +16,12 @@
package com.android.wm.shell.desktopmode;
+import android.graphics.Region;
+
import com.android.wm.shell.common.annotations.ExternalThread;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Interface to interact with desktop mode feature in shell.
@@ -32,7 +35,16 @@ public interface DesktopMode {
* @param listener the listener to add.
* @param callbackExecutor the executor to call the listener on.
*/
- void addListener(DesktopModeTaskRepository.VisibleTasksListener listener,
+ void addVisibleTasksListener(DesktopModeTaskRepository.VisibleTasksListener listener,
Executor callbackExecutor);
+ /**
+ * Adds a consumer to listen for Desktop task corner changes. This is used for gesture
+ * exclusion. The SparseArray contains a list of four corner resize handles mapped to each
+ * desktop task's taskId. The resize handle Rects are stored in the following order:
+ * left-top, left-bottom, right-top, right-bottom.
+ */
+ default void addDesktopGestureExclusionRegionListener(Consumer<Region> listener,
+ Executor callbackExecutor) { }
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
index 2bdbde1b71d4..86ea72582a52 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
@@ -34,6 +34,7 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
import android.content.Context;
import android.database.ContentObserver;
+import android.graphics.Region;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
@@ -69,6 +70,7 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Handles windowing changes when desktop mode system setting changes
@@ -149,11 +151,21 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
* @param listener the listener to add.
* @param callbackExecutor the executor to call the listener on.
*/
- public void addListener(DesktopModeTaskRepository.VisibleTasksListener listener,
+ public void addVisibleTasksListener(DesktopModeTaskRepository.VisibleTasksListener listener,
Executor callbackExecutor) {
mDesktopModeTaskRepository.addVisibleTasksListener(listener, callbackExecutor);
}
+ /**
+ * Adds a listener to track changes to corners of desktop mode tasks.
+ * @param listener the listener to add.
+ * @param callbackExecutor the executor to call the listener on.
+ */
+ public void addTaskCornerListener(Consumer<Region> listener,
+ Executor callbackExecutor) {
+ mDesktopModeTaskRepository.setTaskCornerListener(listener, callbackExecutor);
+ }
+
@VisibleForTesting
void updateDesktopModeActive(boolean active) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeActive: active=%s", active);
@@ -312,6 +324,23 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
}
/**
+ * Update corner rects stored for a specific task
+ * @param taskId task to update
+ * @param taskCorners task's new corner handles
+ */
+ public void onTaskCornersChanged(int taskId, Region taskCorners) {
+ mDesktopModeTaskRepository.updateTaskCorners(taskId, taskCorners);
+ }
+
+ /**
+ * Remove corners saved for a task. Likely used due to task closure.
+ * @param taskId task to remove
+ */
+ public void removeCornersForTask(int taskId) {
+ mDesktopModeTaskRepository.removeTaskCorners(taskId);
+ }
+
+ /**
* Moves a specifc task to the front.
* @param taskInfo the task to show in front.
*/
@@ -426,10 +455,19 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
private final class DesktopModeImpl implements DesktopMode {
@Override
- public void addListener(DesktopModeTaskRepository.VisibleTasksListener listener,
+ public void addVisibleTasksListener(
+ DesktopModeTaskRepository.VisibleTasksListener listener,
+ Executor callbackExecutor) {
+ mMainExecutor.execute(() -> {
+ DesktopModeController.this.addVisibleTasksListener(listener, callbackExecutor);
+ });
+ }
+
+ @Override
+ public void addDesktopGestureExclusionRegionListener(Consumer<Region> listener,
Executor callbackExecutor) {
mMainExecutor.execute(() -> {
- DesktopModeController.this.addListener(listener, callbackExecutor);
+ DesktopModeController.this.addTaskCornerListener(listener, callbackExecutor);
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 47342c9f21ee..12f8ea23ac8f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -16,9 +16,13 @@
package com.android.wm.shell.desktopmode
+import android.graphics.Region
import android.util.ArrayMap
import android.util.ArraySet
+import android.util.SparseArray
+import androidx.core.util.valueIterator
import java.util.concurrent.Executor
+import java.util.function.Consumer
/**
* Keeps track of task data related to desktop mode.
@@ -38,6 +42,10 @@ class DesktopModeTaskRepository {
private val activeTasksListeners = ArraySet<ActiveTasksListener>()
// Track visible tasks separately because a task may be part of the desktop but not visible.
private val visibleTasksListeners = ArrayMap<VisibleTasksListener, Executor>()
+ // Track corners of desktop tasks, used to determine gesture exclusion
+ private val desktopCorners = SparseArray<Region>()
+ private var desktopGestureExclusionListener: Consumer<Region>? = null
+ private var desktopGestureExclusionExecutor: Executor? = null
/**
* Add a [ActiveTasksListener] to be notified of updates to active tasks in the repository.
@@ -56,6 +64,28 @@ class DesktopModeTaskRepository {
}
/**
+ * Add a Consumer which will inform other classes of changes to corners for all Desktop tasks.
+ */
+ fun setTaskCornerListener(cornersListener: Consumer<Region>, executor: Executor) {
+ desktopGestureExclusionListener = cornersListener
+ desktopGestureExclusionExecutor = executor
+ executor.execute {
+ desktopGestureExclusionListener?.accept(calculateDesktopExclusionRegion())
+ }
+ }
+
+ /**
+ * Create a new merged region representative of all corners in all desktop tasks.
+ */
+ private fun calculateDesktopExclusionRegion(): Region {
+ val desktopCornersRegion = Region()
+ desktopCorners.valueIterator().forEach { taskCorners ->
+ desktopCornersRegion.op(taskCorners, Region.Op.UNION)
+ }
+ return desktopCornersRegion
+ }
+
+ /**
* Remove a previously registered [ActiveTasksListener]
*/
fun removeActiveTasksListener(activeTasksListener: ActiveTasksListener) {
@@ -167,6 +197,28 @@ class DesktopModeTaskRepository {
}
/**
+ * Updates the active desktop corners; if desktopCorners has been accepted by
+ * desktopCornersListener, it will be updated in the appropriate classes.
+ */
+ fun updateTaskCorners(taskId: Int, taskCorners: Region) {
+ desktopCorners.put(taskId, taskCorners)
+ desktopGestureExclusionExecutor?.execute {
+ desktopGestureExclusionListener?.accept(calculateDesktopExclusionRegion())
+ }
+ }
+
+ /**
+ * Removes the active desktop corners for the specified task; if desktopCorners has been
+ * accepted by desktopCornersListener, it will be updated in the appropriate classes.
+ */
+ fun removeTaskCorners(taskId: Int) {
+ desktopCorners.delete(taskId)
+ desktopGestureExclusionExecutor?.execute {
+ desktopGestureExclusionListener?.accept(calculateDesktopExclusionRegion())
+ }
+ }
+
+ /**
* Defines interface for classes that can listen to changes for active tasks in desktop mode.
*/
interface ActiveTasksListener {
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 0400963a47e8..0d5602365578 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
@@ -27,6 +27,7 @@ import android.app.WindowConfiguration.WindowingMode
import android.content.Context
import android.graphics.Point
import android.graphics.Rect
+import android.graphics.Region
import android.os.IBinder
import android.os.SystemProperties
import android.view.SurfaceControl
@@ -487,6 +488,20 @@ class DesktopTasksController(
return 2 * getStatusBarHeight(taskInfo)
}
+ /**
+ * Update the corner region for a specified task
+ */
+ fun onTaskCornersChanged(taskId: Int, corner: Region) {
+ desktopModeTaskRepository.updateTaskCorners(taskId, corner)
+ }
+
+ /**
+ * Remove a previously tracked corner region for a specified task.
+ */
+ fun removeCornersForTask(taskId: Int) {
+ desktopModeTaskRepository.removeTaskCorners(taskId)
+ }
+
/**
* Adds a listener to find out about changes in the visibility of freeform tasks.
@@ -494,20 +509,47 @@ class DesktopTasksController(
* @param listener the listener to add.
* @param callbackExecutor the executor to call the listener on.
*/
- fun addListener(listener: VisibleTasksListener, callbackExecutor: Executor) {
+ fun addVisibleTasksListener(listener: VisibleTasksListener, callbackExecutor: Executor) {
desktopModeTaskRepository.addVisibleTasksListener(listener, callbackExecutor)
}
+ /**
+ * Adds a listener to track changes to desktop task corners
+ *
+ * @param listener the listener to add.
+ * @param callbackExecutor the executor to call the listener on.
+ */
+ fun setTaskCornerListener(
+ listener: Consumer<Region>,
+ callbackExecutor: Executor
+ ) {
+ desktopModeTaskRepository.setTaskCornerListener(listener, callbackExecutor)
+ }
+
/** The interface for calls from outside the shell, within the host process. */
@ExternalThread
private inner class DesktopModeImpl : DesktopMode {
- override fun addListener(listener: VisibleTasksListener, callbackExecutor: Executor) {
+ override fun addVisibleTasksListener(
+ listener: VisibleTasksListener,
+ callbackExecutor: Executor
+ ) {
mainExecutor.execute {
- this@DesktopTasksController.addListener(listener, callbackExecutor)
+ this@DesktopTasksController.addVisibleTasksListener(listener, callbackExecutor)
+ }
+ }
+
+ override fun addDesktopGestureExclusionRegionListener(
+ listener: Consumer<Region>,
+ callbackExecutor: Executor
+ ) {
+ mainExecutor.execute {
+ this@DesktopTasksController.setTaskCornerListener(listener, callbackExecutor)
}
}
}
+
+
/** The interface for calls from outside the host process. */
@BinderThread
private class IDesktopModeImpl(private var controller: DesktopTasksController?) :
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b6216b340b38..566c130c7573 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1690,8 +1690,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// Similar to auto-enter-pip transition, we use content overlay when there is no
// source rect hint to enter PiP use bounds animation.
if (sourceHintRect == null) {
+ // We use content overlay when there is no source rect hint to enter PiP use bounds
+ // animation.
+ // TODO(b/272819817): cleanup the null-check and extra logging.
+ final boolean hasTopActivityInfo = mTaskInfo.topActivityInfo != null;
+ if (!hasTopActivityInfo) {
+ ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "%s: TaskInfo.topActivityInfo is null", TAG);
+ }
if (SystemProperties.getBoolean(
- "persist.wm.debug.enable_pip_app_icon_overlay", true)) {
+ "persist.wm.debug.enable_pip_app_icon_overlay", true)
+ && hasTopActivityInfo) {
animator.setAppIconContentOverlay(
mContext, currentBounds, mTaskInfo.topActivityInfo,
mPipBoundsState.getLauncherState().getAppIconSizePx());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index c4a0e9cf5a74..ef5e501917f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -33,7 +33,7 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
WM_SHELL_RECENTS_TRANSITION(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
- Consts.TAG_WM_SHELL),
+ "ShellRecents"),
WM_SHELL_DRAG_AND_DROP(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
WM_SHELL_STARTING_WINDOW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index b55487258220..eb4d2a16c522 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -36,6 +36,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;
+import android.view.Display;
import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget;
@@ -100,6 +101,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
IApplicationThread appThread, IRecentsAnimationRunner listener) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"RecentsTransitionHandler.startRecentsTransition");
+
// only care about latest one.
mAnimApp = appThread;
WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -131,7 +133,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
public WindowContainerTransaction handleRequest(IBinder transition,
TransitionRequestInfo request) {
// do not directly handle requests. Only entry point should be via startRecentsTransition
- Slog.e(TAG, "RecentsTransitionHandler.handleRequest: Unexpected transition request");
+ // TODO: Only log an error if the transition is a recents transition
return null;
}
@@ -169,7 +171,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
SurfaceControl.Transaction t, IBinder mergeTarget,
Transitions.TransitionFinishCallback finishCallback) {
final int targetIdx = findController(mergeTarget);
- if (targetIdx < 0) return;
+ if (targetIdx < 0) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "RecentsTransitionHandler.mergeAnimation: no controller found");
+ return;
+ }
final RecentsController controller = mControllers.get(targetIdx);
controller.merge(info, t, finishCallback);
}
@@ -177,13 +183,20 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
@Override
public void onTransitionConsumed(IBinder transition, boolean aborted,
SurfaceControl.Transaction finishTransaction) {
- final int idx = findController(transition);
- if (idx < 0) return;
- mControllers.get(idx).cancel("onTransitionConsumed");
+ // Only one recents transition can be handled at a time, but currently the first transition
+ // will trigger a no-op in the second transition which holds the active recents animation
+ // runner on the launcher side. For now, cancel all existing animations to ensure we
+ // don't get into a broken state with an orphaned animation runner, and later we can try to
+ // merge the latest transition into the currently running one
+ for (int i = mControllers.size() - 1; i >= 0; i--) {
+ mControllers.get(i).cancel("onTransitionConsumed");
+ }
}
/** There is only one of these and it gets reset on finish. */
private class RecentsController extends IRecentsAnimationController.Stub {
+ private final int mInstanceId;
+
private IRecentsAnimationRunner mListener;
private IBinder.DeathRecipient mDeathHandler;
private Transitions.TransitionFinishCallback mFinishCB = null;
@@ -207,6 +220,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
private int mRecentsTaskId = -1;
private TransitionInfo mInfo = null;
private boolean mOpeningSeparateHome = false;
+ private boolean mPausingSeparateHome = false;
private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
private PictureInPictureSurfaceTransaction mPipTransaction = null;
private IBinder mTransition = null;
@@ -223,10 +237,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
private int mState = STATE_NORMAL;
RecentsController(IRecentsAnimationRunner listener) {
+ mInstanceId = System.identityHashCode(this);
mListener = listener;
mDeathHandler = () -> {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.DeathRecipient: binder died");
+ "[%d] RecentsController.DeathRecipient: binder died", mInstanceId);
finish(mWillFinishToHome, false /* leaveHint */);
};
try {
@@ -239,7 +254,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
void setTransition(IBinder transition) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.setTransition: id=%s", transition);
+ "[%d] RecentsController.setTransition: id=%s", mInstanceId, transition);
mTransition = transition;
}
@@ -251,11 +266,13 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
void cancel(boolean toHome, String reason) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.cancel: toHome=%b reason=%s", toHome, reason);
+ "[%d] RecentsController.cancel: toHome=%b reason=%s",
+ mInstanceId, toHome, reason);
if (mListener != null) {
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.cancel: calling onAnimationCanceled");
+ "[%d] RecentsController.cancel: calling onAnimationCanceled",
+ mInstanceId);
mListener.onAnimationCanceled(null, null);
} catch (RemoteException e) {
Slog.e(TAG, "Error canceling recents animation", e);
@@ -290,7 +307,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
}
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.cancel: calling onAnimationCanceled with snapshots");
+ "[%d] RecentsController.cancel: calling onAnimationCanceled with snapshots",
+ mInstanceId);
mListener.onAnimationCanceled(taskIds, snapshots);
} catch (RemoteException e) {
Slog.e(TAG, "Error canceling recents animation", e);
@@ -300,7 +318,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
}
void cleanUp() {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "RecentsController.cleanup");
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.cleanup", mInstanceId);
if (mListener != null && mDeathHandler != null) {
mListener.asBinder().unlinkToDeath(mDeathHandler, 0 /* flags */);
mDeathHandler = null;
@@ -324,7 +343,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
boolean start(TransitionInfo info, SurfaceControl.Transaction t,
SurfaceControl.Transaction finishT, Transitions.TransitionFinishCallback finishCB) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "RecentsController.start");
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.start", mInstanceId);
if (mListener == null || mTransition == null) {
cleanUp();
return false;
@@ -389,6 +409,10 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
// raise closing (pausing) task to "above" layer so it isn't covered
t.setLayer(target.leash, info.getChanges().size() * 3 - i);
mPausingTasks.add(new TaskState(change, target.leash));
+ if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
+ // This can only happen if we have a separate recents/home (3p launcher)
+ mPausingSeparateHome = true;
+ }
if (taskInfo.pictureInPictureParams != null
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
mPipTask = taskInfo.token;
@@ -409,7 +433,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
t.apply();
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.start: calling onAnimationStart");
+ "[%d] RecentsController.start: calling onAnimationStart", mInstanceId);
mListener.onAnimationStart(this,
apps.toArray(new RemoteAnimationTarget[apps.size()]),
wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
@@ -426,18 +450,20 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
Transitions.TransitionFinishCallback finishCallback) {
if (mFinishCB == null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.merge: skip, no finish callback");
+ "[%d] RecentsController.merge: skip, no finish callback",
+ mInstanceId);
// This was no-op'd (likely a repeated start) and we've already sent finish.
return;
}
if (info.getType() == TRANSIT_SLEEP) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.merge: transit_sleep");
+ "[%d] RecentsController.merge: transit_sleep", mInstanceId);
// A sleep event means we need to stop animations immediately, so cancel here.
cancel("transit_sleep");
return;
}
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "RecentsController.merge");
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.merge", mInstanceId);
ArrayList<TransitionInfo.Change> openingTasks = null;
ArrayList<TransitionInfo.Change> closingTasks = null;
mOpeningSeparateHome = false;
@@ -565,6 +591,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo);
t.reparent(appearedTargets[i].leash, mInfo.getRoot(rootIdx).getLeash());
t.setLayer(appearedTargets[i].leash, layer);
+ // Hide the animation leash, let listener show it.
+ t.hide(appearedTargets[i].leash);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
" opening new taskId=%d", appearedTargets[i].taskId);
mOpeningTasks.add(new TaskState(change, appearedTargets[i].leash));
@@ -592,13 +620,14 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
t.apply();
// not using the incoming anim-only surfaces
info.releaseAnimSurfaces();
- if (appearedTargets == null) return;
- try {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.merge: calling onTasksAppeared");
- mListener.onTasksAppeared(appearedTargets);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error sending appeared tasks to recents animation", e);
+ if (appearedTargets != null) {
+ try {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.merge: calling onTasksAppeared", mInstanceId);
+ mListener.onTasksAppeared(appearedTargets);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending appeared tasks to recents animation", e);
+ }
}
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
}
@@ -620,7 +649,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
public TaskSnapshot screenshotTask(int taskId) {
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.screenshotTask: taskId=%d", taskId);
+ "[%d] RecentsController.screenshotTask: taskId=%d", mInstanceId, taskId);
return ActivityTaskManager.getService().takeTaskSnapshot(taskId);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to screenshot task", e);
@@ -637,14 +666,17 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
mFinishCB != null, enabled);
return;
}
+ final int displayId = mInfo.getRootCount() > 0 ? mInfo.getRoot(0).getDisplayId()
+ : Display.DEFAULT_DISPLAY;
// transient launches don't receive focus automatically. Since we are taking over
// the gesture now, take focus explicitly.
// This also moves recents back to top if the user gestured before a switch
// animation finished.
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.setInputConsumerEnabled: set focus to recents");
- ActivityTaskManager.getService().setFocusedTask(mRecentsTaskId);
+ "[%d] RecentsController.setInputConsumerEnabled: set focus to recents",
+ mInstanceId);
+ ActivityTaskManager.getService().focusTopTask(displayId);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to set focused task", e);
}
@@ -659,7 +691,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
public void setFinishTaskTransaction(int taskId,
PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.setFinishTaskTransaction: taskId=%d", taskId);
+ "[%d] RecentsController.setFinishTaskTransaction: taskId=%d",
+ mInstanceId, taskId);
mExecutor.execute(() -> {
if (mFinishCB == null) return;
mPipTransaction = finishTransaction;
@@ -678,8 +711,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
return;
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.finishInner: toHome=%b userLeaveHint=%b willFinishToHome=%b",
- toHome, sendUserLeaveHint, mWillFinishToHome);
+ "[%d] RecentsController.finishInner: toHome=%b userLeave=%b "
+ + "willFinishToHome=%b state=%d",
+ mInstanceId, toHome, sendUserLeaveHint, mWillFinishToHome, mState);
final Transitions.TransitionFinishCallback finishCB = mFinishCB;
mFinishCB = null;
@@ -690,8 +724,19 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
else wct.restoreTransientOrder(mRecentsTask);
}
- if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " returning to app");
+ if (!toHome
+ // If a recents gesture starts on the 3p launcher, then the 3p launcher is the
+ // live tile (pausing app). If the gesture is "cancelled" we need to return to
+ // 3p launcher instead of "task-switching" away from it.
+ && (!mWillFinishToHome || mPausingSeparateHome)
+ && mPausingTasks != null && mState == STATE_NORMAL) {
+ if (mPausingSeparateHome) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " returning to 3p home");
+ } else {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ " returning to app");
+ }
// The gesture is returning to the pausing-task(s) rather than continuing with
// recents, so end the transition by moving the app back to the top (and also
// re-showing it's task).
@@ -781,7 +826,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
@Override
public void detachNavigationBarFromApp(boolean moveHomeToTop) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "RecentsController.detachNavigationBarFromApp");
+ "[%d] RecentsController.detachNavigationBarFromApp", mInstanceId);
mExecutor.execute(() -> {
if (mTransition == null) return;
try {
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 e4f27240b2cb..cca63ef2b5d6 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
@@ -89,6 +89,9 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.view.Choreographer;
@@ -2390,6 +2393,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (!mMainStage.isActive()) return false;
mSplitLayout.setFreezeDividerWindow(false);
+ final StageChangeRecord record = new StageChangeRecord();
for (int iC = 0; iC < info.getChanges().size(); ++iC) {
final TransitionInfo.Change change = info.getChanges().get(iC);
if (change.getMode() == TRANSIT_CHANGE
@@ -2405,20 +2409,29 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (!stage.containsTask(taskInfo.taskId)) {
Log.w(TAG, "Expected onTaskAppeared on " + stage + " to have been called"
+ " with " + taskInfo.taskId + " before startAnimation().");
+ record.addRecord(stage, true, taskInfo.taskId);
}
} else if (isClosingType(change.getMode())) {
if (stage.containsTask(taskInfo.taskId)) {
+ record.addRecord(stage, false, taskInfo.taskId);
Log.w(TAG, "Expected onTaskVanished on " + stage + " to have been called"
+ " with " + taskInfo.taskId + " before startAnimation().");
}
}
}
- if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
+ // If the size of dismissStages == 1, one of the task is closed without prepare pending
+ // transition, which could happen if all activities were finished after finish top
+ // activity in a task, so the trigger task is null when handleRequest.
+ // Note if the size of dismissStages == 2, it's starting a new task, so don't handle it.
+ final ArraySet<StageTaskListener> dismissStages = record.getShouldDismissedStage();
+ if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0
+ || dismissStages.size() == 1) {
Log.e(TAG, "Somehow removed the last task in a stage outside of a proper "
+ "transition.");
final WindowContainerTransaction wct = new WindowContainerTransaction();
- final int dismissTop = mMainStage.getChildCount() == 0
- ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ final int dismissTop = (dismissStages.size() == 1
+ && getStageType(dismissStages.valueAt(0)) == STAGE_TYPE_MAIN)
+ || mMainStage.getChildCount() == 0 ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
prepareExitSplitScreen(dismissTop, wct);
mSplitTransitions.startDismissTransition(wct, this, dismissTop,
EXIT_REASON_UNKNOWN);
@@ -2445,6 +2458,57 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
finishCallback);
}
+ static class StageChangeRecord {
+ static class StageChange {
+ final StageTaskListener mStageTaskListener;
+ final IntArray mAddedTaskId = new IntArray();
+ final IntArray mRemovedTaskId = new IntArray();
+ StageChange(StageTaskListener stage) {
+ mStageTaskListener = stage;
+ }
+
+ boolean shouldDismissStage() {
+ if (mAddedTaskId.size() > 0 || mRemovedTaskId.size() == 0) {
+ return false;
+ }
+ int removeChildTaskCount = 0;
+ for (int i = mRemovedTaskId.size() - 1; i >= 0; --i) {
+ if (mStageTaskListener.containsTask(mRemovedTaskId.get(i))) {
+ ++removeChildTaskCount;
+ }
+ }
+ return removeChildTaskCount == mStageTaskListener.getChildCount();
+ }
+ }
+ private final ArrayMap<StageTaskListener, StageChange> mChanges = new ArrayMap<>();
+
+ void addRecord(StageTaskListener stage, boolean open, int taskId) {
+ final StageChange next;
+ if (!mChanges.containsKey(stage)) {
+ next = new StageChange(stage);
+ mChanges.put(stage, next);
+ } else {
+ next = mChanges.get(stage);
+ }
+ if (open) {
+ next.mAddedTaskId.add(taskId);
+ } else {
+ next.mRemovedTaskId.add(taskId);
+ }
+ }
+
+ ArraySet<StageTaskListener> getShouldDismissedStage() {
+ final ArraySet<StageTaskListener> dismissTarget = new ArraySet<>();
+ for (int i = mChanges.size() - 1; i >= 0; --i) {
+ final StageChange change = mChanges.valueAt(i);
+ if (change.shouldDismissStage()) {
+ dismissTarget.add(change.mStageTaskListener);
+ }
+ }
+ return dismissTarget;
+ }
+ }
+
/** Starts the pending transition animation. */
public boolean startPendingAnimation(@NonNull IBinder transition,
@NonNull TransitionInfo info,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 36c9077a197b..7991c529aa49 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -388,11 +388,6 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
}
// Sync Transactions can't operate simultaneously with shell transition collection.
if (isUsingShellTransitions()) {
- if (mTaskViewTransitions.hasPending()) {
- // There is already a transition in-flight. The window bounds will be synced
- // once it is complete.
- return;
- }
mTaskViewTransitions.setTaskBounds(this, boundsOnScreen);
return;
}
@@ -489,12 +484,14 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
finishTransaction.reparent(mTaskLeash, mSurfaceControl)
.setPosition(mTaskLeash, 0, 0)
.apply();
-
+ mTaskViewTransitions.updateBoundsState(this, mTaskViewBase.getCurrentBoundsOnScreen());
+ mTaskViewTransitions.updateVisibilityState(this, true /* visible */);
wct.setBounds(mTaskToken, mTaskViewBase.getCurrentBoundsOnScreen());
} else {
// The surface has already been destroyed before the task has appeared,
// so go ahead and hide the task entirely
wct.setHidden(mTaskToken, true /* hidden */);
+ mTaskViewTransitions.updateVisibilityState(this, false /* visible */);
// listener callback is below
}
if (newTask) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 3b1ce49ebdc7..81d69a4fa611 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -26,6 +26,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.graphics.Rect;
import android.os.IBinder;
+import android.util.ArrayMap;
import android.util.Slog;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -33,10 +34,13 @@ import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
+import androidx.annotation.VisibleForTesting;
+
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.TransitionUtil;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Handles Shell Transitions that involve TaskView tasks.
@@ -44,7 +48,8 @@ import java.util.ArrayList;
public class TaskViewTransitions implements Transitions.TransitionHandler {
static final String TAG = "TaskViewTransitions";
- private final ArrayList<TaskViewTaskController> mTaskViews = new ArrayList<>();
+ private final ArrayMap<TaskViewTaskController, TaskViewRequestedState> mTaskViews =
+ new ArrayMap<>();
private final ArrayList<PendingTransition> mPending = new ArrayList<>();
private final Transitions mTransitions;
private final boolean[] mRegistered = new boolean[]{ false };
@@ -54,7 +59,8 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
* in-flight (collecting) at a time (because otherwise, the operations could get merged into
* a single transition). So, keep a queue here until we add a queue in server-side.
*/
- private static class PendingTransition {
+ @VisibleForTesting
+ static class PendingTransition {
final @WindowManager.TransitionType int mType;
final WindowContainerTransaction mWct;
final @NonNull TaskViewTaskController mTaskView;
@@ -78,6 +84,14 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
}
}
+ /**
+ * Visibility and bounds state that has been requested for a {@link TaskViewTaskController}.
+ */
+ private static class TaskViewRequestedState {
+ boolean mVisible;
+ Rect mBounds = new Rect();
+ }
+
public TaskViewTransitions(Transitions transitions) {
mTransitions = transitions;
// Defer registration until the first TaskView because we want this to be the "first" in
@@ -92,7 +106,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
mTransitions.addHandler(this);
}
}
- mTaskViews.add(tv);
+ mTaskViews.put(tv, new TaskViewRequestedState());
}
void removeTaskView(TaskViewTaskController tv) {
@@ -105,24 +119,30 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
}
/**
- * Looks through the pending transitions for one matching `taskView`.
+ * Looks through the pending transitions for a closing transaction that matches the provided
+ * `taskView`.
* @param taskView the pending transition should be for this.
- * @param closing When true, this only returns a pending transition of the close/hide type.
- * Otherwise it selects open/show.
- * @param latest When true, this will only check the most-recent pending transition for the
- * specified taskView. If it doesn't match `closing`, this will return null even
- * if there is a match earlier. The idea behind this is to check the state of
- * the taskviews "as if all transitions already happened".
*/
- private PendingTransition findPending(TaskViewTaskController taskView, boolean closing,
- boolean latest) {
+ private PendingTransition findPendingCloseTransition(TaskViewTaskController taskView) {
for (int i = mPending.size() - 1; i >= 0; --i) {
if (mPending.get(i).mTaskView != taskView) continue;
- if (TransitionUtil.isClosingType(mPending.get(i).mType) == closing) {
+ if (TransitionUtil.isClosingType(mPending.get(i).mType)) {
return mPending.get(i);
}
- if (latest) {
- return null;
+ }
+ return null;
+ }
+
+ /**
+ * Looks through the pending transitions for one matching `taskView`.
+ * @param taskView the pending transition should be for this.
+ * @param type the type of transition it's looking for
+ */
+ PendingTransition findPending(TaskViewTaskController taskView, int type) {
+ for (int i = mPending.size() - 1; i >= 0; --i) {
+ if (mPending.get(i).mTaskView != taskView) continue;
+ if (mPending.get(i).mType == type) {
+ return mPending.get(i);
}
}
return null;
@@ -152,7 +172,7 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
if (taskView == null) return null;
// Opening types should all be initiated by shell
if (!TransitionUtil.isClosingType(request.getType())) return null;
- PendingTransition pending = findPending(taskView, true /* closing */, false /* latest */);
+ PendingTransition pending = findPendingCloseTransition(taskView);
if (pending == null) {
pending = new PendingTransition(request.getType(), null, taskView, null /* cookie */);
}
@@ -166,9 +186,9 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
private TaskViewTaskController findTaskView(ActivityManager.RunningTaskInfo taskInfo) {
for (int i = 0; i < mTaskViews.size(); ++i) {
- if (mTaskViews.get(i).getTaskInfo() == null) continue;
- if (taskInfo.token.equals(mTaskViews.get(i).getTaskInfo().token)) {
- return mTaskViews.get(i);
+ if (mTaskViews.keyAt(i).getTaskInfo() == null) continue;
+ if (taskInfo.token.equals(mTaskViews.keyAt(i).getTaskInfo().token)) {
+ return mTaskViews.keyAt(i);
}
}
return null;
@@ -176,30 +196,53 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
void startTaskView(@NonNull WindowContainerTransaction wct,
@NonNull TaskViewTaskController taskView, @NonNull IBinder launchCookie) {
+ updateVisibilityState(taskView, true /* visible */);
mPending.add(new PendingTransition(TRANSIT_OPEN, wct, taskView, launchCookie));
startNextTransition();
}
void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) {
- PendingTransition pending = findPending(taskView, !visible, true /* latest */);
- if (pending != null) {
- // Already opening or creating a task, so no need to do anything here.
- return;
- }
+ if (mTaskViews.get(taskView).mVisible == visible) return;
if (taskView.getTaskInfo() == null) {
// Nothing to update, task is not yet available
return;
}
+ mTaskViews.get(taskView).mVisible = visible;
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setHidden(taskView.getTaskInfo().token, !visible /* hidden */);
- pending = new PendingTransition(
+ wct.setBounds(taskView.getTaskInfo().token, mTaskViews.get(taskView).mBounds);
+ PendingTransition pending = new PendingTransition(
visible ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK, wct, taskView, null /* cookie */);
mPending.add(pending);
startNextTransition();
// visibility is reported in transition.
}
+ void updateBoundsState(TaskViewTaskController taskView, Rect boundsOnScreen) {
+ TaskViewRequestedState state = mTaskViews.get(taskView);
+ state.mBounds.set(boundsOnScreen);
+ }
+
+ void updateVisibilityState(TaskViewTaskController taskView, boolean visible) {
+ TaskViewRequestedState state = mTaskViews.get(taskView);
+ state.mVisible = visible;
+ }
+
void setTaskBounds(TaskViewTaskController taskView, Rect boundsOnScreen) {
+ TaskViewRequestedState state = mTaskViews.get(taskView);
+ if (Objects.equals(boundsOnScreen, state.mBounds)) {
+ return;
+ }
+ state.mBounds.set(boundsOnScreen);
+ if (!state.mVisible) {
+ // Task view isn't visible, the bounds will next visibility update.
+ return;
+ }
+ if (hasPending()) {
+ // There is already a transition in-flight, the window bounds will be set in
+ // prepareOpenAnimation.
+ return;
+ }
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setBounds(taskView.getTaskInfo().token, boundsOnScreen);
mPending.add(new PendingTransition(TRANSIT_CHANGE, wct, taskView, null /* cookie */));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 5a92f7830194..7c729a46b679 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -235,6 +235,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
private TransitionInfo subCopy(@NonNull TransitionInfo info,
@WindowManager.TransitionType int newType, boolean withChanges) {
final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0);
+ out.setTrack(info.getTrack());
out.setDebugId(info.getDebugId());
if (withChanges) {
for (int i = 0; i < info.getChanges().size(); ++i) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index bcc37baa5b00..0f4645c0fdab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -122,14 +122,14 @@ public class TransitionAnimationHelper {
? R.styleable.WindowAnimation_taskToFrontEnterAnimation
: R.styleable.WindowAnimation_taskToFrontExitAnimation;
} else if (type == TRANSIT_CLOSE) {
- if (isTask) {
+ if ((changeFlags & FLAG_TRANSLUCENT) != 0 && !enter) {
+ translucent = true;
+ }
+ if (isTask && !translucent) {
animAttr = enter
? R.styleable.WindowAnimation_taskCloseEnterAnimation
: R.styleable.WindowAnimation_taskCloseExitAnimation;
} else {
- if ((changeFlags & FLAG_TRANSLUCENT) != 0 && !enter) {
- translucent = true;
- }
animAttr = enter
? R.styleable.WindowAnimation_activityCloseEnterAnimation
: R.styleable.WindowAnimation_activityCloseExitAnimation;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 08b0bf74f413..5c8791effe18 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -90,14 +90,21 @@ import java.util.Arrays;
* Basically: --start--> PENDING --onTransitionReady--> READY --play--> ACTIVE --finish--> |
* --merge--> MERGED --^
*
- * At the moment, only one transition can be animating at a time. While a transition is animating,
- * transitions will be queued in the "ready" state for their turn. At the same time, whenever a
- * transition makes it to the head of the "ready" queue, it will attempt to merge to with the
- * "active" transition. If the merge succeeds, it will be moved to the "active" transition's
- * "merged" and then the next "ready" transition can attempt to merge.
+ * The READY and beyond lifecycle is managed per "track". Within a track, all the animations are
+ * serialized as described; however, multiple tracks can play simultaneously. This implies that,
+ * within a track, only one transition can be animating ("active") at a time.
*
- * Once the "active" transition animation is finished, it will be removed from the "active" list
- * and then the next "ready" transition can play.
+ * While a transition is animating in a track, transitions dispatched to the track will be queued
+ * in the "ready" state for their turn. At the same time, whenever a transition makes it to the
+ * head of the "ready" queue, it will attempt to merge to with the "active" transition. If the
+ * merge succeeds, it will be moved to the "active" transition's "merged" list and then the next
+ * "ready" transition can attempt to merge. Once the "active" transition animation is finished,
+ * the next "ready" transition can play.
+ *
+ * Track assignments are expected to be provided by WMCore and this generally tries to maintain
+ * the same assignments. If, however, WMCore decides that a transition conflicts with >1 active
+ * track, it will be marked as SYNC. This means that all currently active tracks must be flushed
+ * before the SYNC transition can play.
*/
public class Transitions implements RemoteCallable<Transitions> {
static final String TAG = "ShellTransitions";
@@ -172,12 +179,15 @@ public class Transitions implements RemoteCallable<Transitions> {
private float mTransitionAnimationScaleSetting = 1.0f;
/**
- * How much time we allow for an animation to finish itself on sleep. If it takes longer, we
+ * How much time we allow for an animation to finish itself on sync. If it takes longer, we
* will force-finish it (on this end) which may leave it in a bad state but won't hang the
* device. This needs to be pretty small because it is an allowance for each queued animation,
* however it can't be too small since there is some potential IPC involved.
*/
- private static final int SLEEP_ALLOWANCE_MS = 120;
+ private static final int SYNC_ALLOWANCE_MS = 120;
+
+ /** For testing only. Disables the force-finish timeout on sync. */
+ private boolean mDisableForceSync = false;
private static final class ActiveTransition {
IBinder mToken;
@@ -190,23 +200,45 @@ public class Transitions implements RemoteCallable<Transitions> {
/** Ordered list of transitions which have been merged into this one. */
private ArrayList<ActiveTransition> mMerged;
+ boolean isSync() {
+ return (mInfo.getFlags() & TransitionInfo.FLAG_SYNC) != 0;
+ }
+
+ int getTrack() {
+ return mInfo != null ? mInfo.getTrack() : -1;
+ }
+
@Override
public String toString() {
if (mInfo != null && mInfo.getDebugId() >= 0) {
- return "(#" + mInfo.getDebugId() + ")" + mToken;
+ return "(#" + mInfo.getDebugId() + ")" + mToken + "@" + getTrack();
}
- return mToken.toString();
+ return mToken.toString() + "@" + getTrack();
+ }
+ }
+
+ private static class Track {
+ /** Keeps track of transitions which are ready to play but still waiting for their turn. */
+ final ArrayList<ActiveTransition> mReadyTransitions = new ArrayList<>();
+
+ /** The currently playing transition in this track. */
+ ActiveTransition mActiveTransition = null;
+
+ boolean isIdle() {
+ return mActiveTransition == null && mReadyTransitions.isEmpty();
}
}
/** Keeps track of transitions which have been started, but aren't ready yet. */
private final ArrayList<ActiveTransition> mPendingTransitions = new ArrayList<>();
- /** Keeps track of transitions which are ready to play but still waiting for their turn. */
- private final ArrayList<ActiveTransition> mReadyTransitions = new ArrayList<>();
+ /**
+ * Transitions which are ready to play, but haven't been sent to a track yet because a sync
+ * is ongoing.
+ */
+ private final ArrayList<ActiveTransition> mReadyDuringSync = new ArrayList<>();
- /** Keeps track of currently playing transitions. For now, there can only be 1 max. */
- private final ArrayList<ActiveTransition> mActiveTransitions = new ArrayList<>();
+ private final ArrayList<Track> mTracks = new ArrayList<>();
public Transitions(@NonNull Context context,
@NonNull ShellInit shellInit,
@@ -374,14 +406,17 @@ public class Transitions implements RemoteCallable<Transitions> {
* will be executed when the last active transition is finished.
*/
public void runOnIdle(Runnable runnable) {
- if (mActiveTransitions.isEmpty() && mPendingTransitions.isEmpty()
- && mReadyTransitions.isEmpty()) {
+ if (isIdle()) {
runnable.run();
} else {
mRunWhenIdleQueue.add(runnable);
}
}
+ void setDisableForceSyncForTest(boolean disable) {
+ mDisableForceSync = disable;
+ }
+
/**
* Sets up visibility/alpha/transforms to resemble the starting state of an animation.
*/
@@ -542,6 +577,13 @@ public class Transitions implements RemoteCallable<Transitions> {
return true;
}
+ private Track getOrCreateTrack(int trackId) {
+ while (trackId >= mTracks.size()) {
+ mTracks.add(new Track());
+ }
+ return mTracks.get(trackId);
+ }
+
@VisibleForTesting
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
@@ -554,29 +596,58 @@ public class Transitions implements RemoteCallable<Transitions> {
+ Arrays.toString(mPendingTransitions.stream().map(
activeTransition -> activeTransition.mToken).toArray()));
}
- if (activeIdx > 0) {
- Log.e(TAG, "Transition became ready out-of-order " + mPendingTransitions.get(activeIdx)
- + ". Expected order: " + Arrays.toString(mPendingTransitions.stream().map(
- activeTransition -> activeTransition.mToken).toArray()));
- }
// Move from pending to ready
final ActiveTransition active = mPendingTransitions.remove(activeIdx);
- mReadyTransitions.add(active);
active.mInfo = info;
active.mStartT = t;
active.mFinishT = finishT;
-
- for (int i = 0; i < mObservers.size(); ++i) {
- mObservers.get(i).onTransitionReady(transitionToken, info, t, finishT);
+ if (activeIdx > 0) {
+ Log.i(TAG, "Transition might be ready out-of-order " + activeIdx + " for " + active
+ + ". This is ok if it's on a different track.");
}
+ if (!mReadyDuringSync.isEmpty()) {
+ mReadyDuringSync.add(active);
+ } else {
+ dispatchReady(active);
+ }
+ }
- if (info.getType() == TRANSIT_SLEEP) {
- if (activeIdx > 0 || !mActiveTransitions.isEmpty() || mReadyTransitions.size() > 1) {
+ /**
+ * Returns true if dispatching succeeded, otherwise false. Dispatching can fail if it is
+ * blocked by a sync or sleep.
+ */
+ boolean dispatchReady(ActiveTransition active) {
+ final TransitionInfo info = active.mInfo;
+
+ if (info.getType() == TRANSIT_SLEEP || active.isSync()) {
+ // Adding to *front*! If we are here, it means that it was pulled off the front
+ // so we are just putting it back; or, it is the first one so it doesn't matter.
+ mReadyDuringSync.add(0, active);
+ boolean hadPreceding = false;
+ // Now flush all the tracks.
+ for (int i = 0; i < mTracks.size(); ++i) {
+ final Track tr = mTracks.get(i);
+ if (tr.isIdle()) continue;
+ hadPreceding = true;
// Sleep starts a process of forcing all prior transitions to finish immediately
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Start finish-for-sleep");
- finishForSleep(null /* forceFinish */);
- return;
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Start finish-for-sync track %d", i);
+ finishForSync(i, null /* forceFinish */);
}
+ if (hadPreceding) {
+ return false;
+ }
+ // Actually able to process the sleep now, so re-remove it from the queue and continue
+ // the normal flow.
+ mReadyDuringSync.remove(active);
+ }
+
+ final Track track = getOrCreateTrack(info.getTrack());
+ track.mReadyTransitions.add(active);
+
+ for (int i = 0; i < mObservers.size(); ++i) {
+ mObservers.get(i).onTransitionReady(
+ active.mToken, info, active.mStartT, active.mFinishT);
}
if (info.getRootCount() == 0 && !alwaysReportToKeyguard(info)) {
@@ -585,7 +656,7 @@ public class Transitions implements RemoteCallable<Transitions> {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots in %s so"
+ " abort", active);
onAbort(active);
- return;
+ return true;
}
final int changeSize = info.getChanges().size();
@@ -615,16 +686,17 @@ public class Transitions implements RemoteCallable<Transitions> {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"Non-visible anim so abort: %s", active);
onAbort(active);
- return;
+ return true;
}
setupStartState(active.mInfo, active.mStartT, active.mFinishT);
- if (mReadyTransitions.size() > 1) {
+ if (track.mReadyTransitions.size() > 1) {
// There are already transitions waiting in the queue, so just return.
- return;
+ return true;
}
- processReadyQueue();
+ processReadyQueue(track);
+ return true;
}
/**
@@ -644,25 +716,53 @@ public class Transitions implements RemoteCallable<Transitions> {
return false;
}
- void processReadyQueue() {
- if (mReadyTransitions.isEmpty()) {
- // Check if idle.
- if (mActiveTransitions.isEmpty() && mPendingTransitions.isEmpty()) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition "
- + "animations finished");
- // Run all runnables from the run-when-idle queue.
- for (int i = 0; i < mRunWhenIdleQueue.size(); i++) {
- mRunWhenIdleQueue.get(i).run();
+ private boolean areTracksIdle() {
+ for (int i = 0; i < mTracks.size(); ++i) {
+ if (!mTracks.get(i).isIdle()) return false;
+ }
+ return true;
+ }
+
+ private boolean isAnimating() {
+ return !mReadyDuringSync.isEmpty() || !areTracksIdle();
+ }
+
+ private boolean isIdle() {
+ return mPendingTransitions.isEmpty() && !isAnimating();
+ }
+
+ void processReadyQueue(Track track) {
+ if (track.mReadyTransitions.isEmpty()) {
+ if (track.mActiveTransition == null) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Track %d became idle",
+ mTracks.indexOf(track));
+ if (areTracksIdle()) {
+ if (!mReadyDuringSync.isEmpty()) {
+ // Dispatch everything unless we hit another sync
+ while (!mReadyDuringSync.isEmpty()) {
+ ActiveTransition next = mReadyDuringSync.remove(0);
+ boolean success = dispatchReady(next);
+ // Hit a sync or sleep, so stop dispatching.
+ if (!success) break;
+ }
+ } else if (mPendingTransitions.isEmpty()) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition "
+ + "animations finished");
+ // Run all runnables from the run-when-idle queue.
+ for (int i = 0; i < mRunWhenIdleQueue.size(); i++) {
+ mRunWhenIdleQueue.get(i).run();
+ }
+ mRunWhenIdleQueue.clear();
+ }
}
- mRunWhenIdleQueue.clear();
}
return;
}
- final ActiveTransition ready = mReadyTransitions.get(0);
- if (mActiveTransitions.isEmpty()) {
- // The normal case, just play it (currently we only support 1 active transition).
- mReadyTransitions.remove(0);
- mActiveTransitions.add(ready);
+ final ActiveTransition ready = track.mReadyTransitions.get(0);
+ if (track.mActiveTransition == null) {
+ // The normal case, just play it.
+ track.mReadyTransitions.remove(0);
+ track.mActiveTransition = ready;
if (ready.mAborted) {
// finish now since there's nothing to animate. Calls back into processReadyQueue
onFinish(ready, null, null);
@@ -670,11 +770,11 @@ public class Transitions implements RemoteCallable<Transitions> {
}
playTransition(ready);
// Attempt to merge any more queued-up transitions.
- processReadyQueue();
+ processReadyQueue(track);
return;
}
// An existing animation is playing, so see if we can merge.
- final ActiveTransition playing = mActiveTransitions.get(0);
+ final ActiveTransition playing = track.mActiveTransition;
if (ready.mAborted) {
// record as merged since it is no-op. Calls back into processReadyQueue
onMerged(playing, ready);
@@ -688,18 +788,23 @@ public class Transitions implements RemoteCallable<Transitions> {
}
private void onMerged(@NonNull ActiveTransition playing, @NonNull ActiveTransition merged) {
+ if (playing.getTrack() != merged.getTrack()) {
+ throw new IllegalStateException("Can't merge across tracks: " + merged + " into "
+ + playing);
+ }
+ final Track track = mTracks.get(playing.getTrack());
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
merged, playing);
int readyIdx = 0;
- if (mReadyTransitions.isEmpty() || mReadyTransitions.get(0) != merged) {
+ if (track.mReadyTransitions.isEmpty() || track.mReadyTransitions.get(0) != merged) {
Log.e(TAG, "Merged transition out-of-order? " + merged);
- readyIdx = mReadyTransitions.indexOf(merged);
+ readyIdx = track.mReadyTransitions.indexOf(merged);
if (readyIdx < 0) {
Log.e(TAG, "Merged a transition that is no-longer queued? " + merged);
return;
}
}
- mReadyTransitions.remove(readyIdx);
+ track.mReadyTransitions.remove(readyIdx);
if (playing.mMerged == null) {
playing.mMerged = new ArrayList<>();
}
@@ -712,7 +817,7 @@ public class Transitions implements RemoteCallable<Transitions> {
mObservers.get(i).onTransitionMerged(merged.mToken, playing.mToken);
}
// See if we should merge another transition.
- processReadyQueue();
+ processReadyQueue(track);
}
private void playTransition(@NonNull ActiveTransition active) {
@@ -781,6 +886,7 @@ public class Transitions implements RemoteCallable<Transitions> {
/** Aborts a transition. This will still queue it up to maintain order. */
private void onAbort(ActiveTransition transition) {
+ final Track track = mTracks.get(transition.getTrack());
// apply immediately since they may be "parallel" operations: We currently we use abort for
// thing which are independent to other transitions (like starting-window transfer).
transition.mStartT.apply();
@@ -796,11 +902,11 @@ public class Transitions implements RemoteCallable<Transitions> {
releaseSurfaces(transition.mInfo);
// This still went into the queue (to maintain the correct finish ordering).
- if (mReadyTransitions.size() > 1) {
+ if (track.mReadyTransitions.size() > 1) {
// There are already transitions waiting in the queue, so just return.
return;
}
- processReadyQueue();
+ processReadyQueue(track);
}
/**
@@ -815,17 +921,14 @@ public class Transitions implements RemoteCallable<Transitions> {
private void onFinish(ActiveTransition active,
@Nullable WindowContainerTransaction wct,
@Nullable WindowContainerTransactionCallback wctCB) {
- int activeIdx = mActiveTransitions.indexOf(active);
- if (activeIdx < 0) {
+ final Track track = mTracks.get(active.getTrack());
+ if (track.mActiveTransition != active) {
Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
+ " a handler didn't properly deal with a merge. " + active,
new RuntimeException());
return;
- } else if (activeIdx != 0) {
- // Relevant right now since we only allow 1 active transition at a time.
- Log.e(TAG, "Finishing a transition out of order. " + active);
}
- mActiveTransitions.remove(activeIdx);
+ track.mActiveTransition = null;
for (int i = 0; i < mObservers.size(); ++i) {
mObservers.get(i).onTransitionFinished(active.mToken, active.mAborted);
@@ -877,18 +980,20 @@ public class Transitions implements RemoteCallable<Transitions> {
}
// Now that this is done, check the ready queue for more work.
- processReadyQueue();
+ processReadyQueue(track);
}
private boolean isTransitionKnown(IBinder token) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
if (mPendingTransitions.get(i).mToken == token) return true;
}
- for (int i = 0; i < mReadyTransitions.size(); ++i) {
- if (mReadyTransitions.get(i).mToken == token) return true;
- }
- for (int i = 0; i < mActiveTransitions.size(); ++i) {
- final ActiveTransition active = mActiveTransitions.get(i);
+ for (int t = 0; t < mTracks.size(); ++t) {
+ final Track tr = mTracks.get(t);
+ for (int i = 0; i < tr.mReadyTransitions.size(); ++i) {
+ if (tr.mReadyTransitions.get(i).mToken == token) return true;
+ }
+ final ActiveTransition active = tr.mActiveTransition;
+ if (active == null) continue;
if (active.mToken == token) return true;
if (active.mMerged == null) continue;
for (int m = 0; m < active.mMerged.size(); ++m) {
@@ -963,7 +1068,7 @@ public class Transitions implements RemoteCallable<Transitions> {
*
* This works by "merging" the sleep transition into the currently-playing transition (even if
* its out-of-order) -- turning SLEEP into a signal. If the playing transition doesn't finish
- * within `SLEEP_ALLOWANCE_MS` from this merge attempt, this will then finish it directly (and
+ * within `SYNC_ALLOWANCE_MS` from this merge attempt, this will then finish it directly (and
* send an abort/consumed message).
*
* This is then repeated until there are no more pending sleep transitions.
@@ -971,48 +1076,53 @@ public class Transitions implements RemoteCallable<Transitions> {
* @param forceFinish When non-null, this is the transition that we last sent the SLEEP merge
* signal to -- so it will be force-finished if it's still running.
*/
- private void finishForSleep(@Nullable ActiveTransition forceFinish) {
- if ((mActiveTransitions.isEmpty() && mReadyTransitions.isEmpty())
- || mSleepHandler.mSleepTransitions.isEmpty()) {
+ private void finishForSync(int trackIdx, @Nullable ActiveTransition forceFinish) {
+ final Track track = mTracks.get(trackIdx);
+ if (forceFinish != null) {
+ final Track trk = mTracks.get(forceFinish.getTrack());
+ if (trk != track) {
+ Log.e(TAG, "finishForSleep: mismatched Tracks between forceFinish and logic "
+ + forceFinish.getTrack() + " vs " + trackIdx);
+ }
+ if (trk.mActiveTransition == forceFinish) {
+ Log.e(TAG, "Forcing transition to finish due to sync timeout: " + forceFinish);
+ forceFinish.mAborted = true;
+ // Last notify of it being consumed. Note: mHandler should never be null,
+ // but check just to be safe.
+ if (forceFinish.mHandler != null) {
+ forceFinish.mHandler.onTransitionConsumed(
+ forceFinish.mToken, true /* aborted */, null /* finishTransaction */);
+ }
+ onFinish(forceFinish, null, null);
+ }
+ }
+ if (track.isIdle() || mReadyDuringSync.isEmpty()) {
// Done finishing things.
- // Prevent any weird leaks... shouldn't happen though.
- mSleepHandler.mSleepTransitions.clear();
return;
}
- if (forceFinish != null && mActiveTransitions.contains(forceFinish)) {
- Log.e(TAG, "Forcing transition to finish due to sleep timeout: " + forceFinish);
- forceFinish.mAborted = true;
- // Last notify of it being consumed. Note: mHandler should never be null,
- // but check just to be safe.
- if (forceFinish.mHandler != null) {
- forceFinish.mHandler.onTransitionConsumed(
- forceFinish.mToken, true /* aborted */, null /* finishTransaction */);
- }
- onFinish(forceFinish, null, null);
- }
final SurfaceControl.Transaction dummyT = new SurfaceControl.Transaction();
final TransitionInfo dummyInfo = new TransitionInfo(TRANSIT_SLEEP, 0 /* flags */);
- while (!mActiveTransitions.isEmpty() && !mSleepHandler.mSleepTransitions.isEmpty()) {
- final ActiveTransition playing = mActiveTransitions.get(0);
- int sleepIdx = findByToken(mReadyTransitions, mSleepHandler.mSleepTransitions.get(0));
- if (sleepIdx >= 0) {
- // Try to signal that we are sleeping by attempting to merge the sleep transition
- // into the playing one.
- final ActiveTransition nextSleep = mReadyTransitions.get(sleepIdx);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt to merge SLEEP %s"
- + " into %s", nextSleep, playing);
- playing.mHandler.mergeAnimation(nextSleep.mToken, dummyInfo, dummyT,
- playing.mToken, (wct, cb) -> {});
- } else {
- Log.e(TAG, "Couldn't find sleep transition in ready list: "
- + mSleepHandler.mSleepTransitions.get(0));
+ while (track.mActiveTransition != null && !mReadyDuringSync.isEmpty()) {
+ final ActiveTransition playing = track.mActiveTransition;
+ final ActiveTransition nextSync = mReadyDuringSync.get(0);
+ if (!nextSync.isSync()) {
+ Log.e(TAG, "Somehow blocked on a non-sync transition? " + nextSync);
}
+ // Attempt to merge a SLEEP info to signal that the playing transition needs to
+ // fast-forward.
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt to merge sync %s"
+ + " into %s via a SLEEP proxy", nextSync, playing);
+ playing.mHandler.mergeAnimation(nextSync.mToken, dummyInfo, dummyT,
+ playing.mToken, (wct, cb) -> {});
// it's possible to complete immediately. If that happens, just repeat the signal
// loop until we either finish everything or start playing an animation that isn't
// finishing immediately.
- if (!mActiveTransitions.isEmpty() && mActiveTransitions.get(0) == playing) {
- // Give it a (very) short amount of time to process it before forcing.
- mMainExecutor.executeDelayed(() -> finishForSleep(playing), SLEEP_ALLOWANCE_MS);
+ if (track.mActiveTransition == playing) {
+ if (!mDisableForceSync) {
+ // Give it a short amount of time to process it before forcing.
+ mMainExecutor.executeDelayed(() -> finishForSync(trackIdx, playing),
+ SYNC_ALLOWANCE_MS);
+ }
break;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 49a5eac5fd07..8fb56fc2561b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -36,6 +36,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.Region;
import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.IBinder;
@@ -69,6 +70,7 @@ import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.TaskCornersListener;
import java.util.Optional;
import java.util.function.Supplier;
@@ -95,9 +97,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
+ private final TaskCornersListener mCornersListener = new TaskCornersListenerImpl();
+
private final SparseArray<DesktopModeWindowDecoration> mWindowDecorByTaskId =
new SparseArray<>();
- private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
+ private final DragListenerImpl mDragStartListener = new DragListenerImpl();
private final InputMonitorFactory mInputMonitorFactory;
private TaskOperations mTaskOperations;
private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
@@ -779,6 +783,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final DesktopModeTouchEventListener touchEventListener =
new DesktopModeTouchEventListener(taskInfo, taskPositioner);
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
+ windowDecoration.setCornersListener(mCornersListener);
windowDecoration.setDragPositioningCallback(taskPositioner);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT,
@@ -786,7 +791,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
incrementEventReceiverTasks(taskInfo.displayId);
}
- private class DragStartListenerImpl implements TaskPositioner.DragStartListener {
+ private class DragListenerImpl implements TaskPositioner.DragStartListener {
@Override
public void onDragStart(int taskId) {
mWindowDecorByTaskId.get(taskId).closeHandleMenu();
@@ -798,6 +803,22 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return inputManager.monitorGestureInput("caption-touch", context.getDisplayId());
}
}
+
+ private class TaskCornersListenerImpl
+ implements DesktopModeWindowDecoration.TaskCornersListener {
+
+ @Override
+ public void onTaskCornersChanged(int taskId, Region corner) {
+ mDesktopModeController.ifPresent(d -> d.onTaskCornersChanged(taskId, corner));
+ mDesktopTasksController.ifPresent(d -> d.onTaskCornersChanged(taskId, corner));
+ }
+
+ @Override
+ public void onTaskCornersRemoved(int taskId) {
+ mDesktopModeController.ifPresent(d -> d.removeCornersForTask(taskId));
+ mDesktopTasksController.ifPresent(d -> d.removeCornersForTask(taskId));
+ }
+ }
}
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 af3fb0ead55c..619e963aaea6 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
@@ -30,6 +30,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
+import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.Log;
@@ -80,6 +81,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
new WindowDecoration.RelayoutResult<>();
+ private final Point mPositionInParent = new Point();
private final PointF mHandleMenuAppInfoPillPosition = new PointF();
private final PointF mHandleMenuWindowingPillPosition = new PointF();
private final PointF mHandleMenuMoreActionsPillPosition = new PointF();
@@ -92,6 +94,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private Drawable mAppIcon;
private CharSequence mAppName;
+ private TaskCornersListener mCornersListener;
+
private int mMenuWidth;
private int mMarginMenuTop;
private int mMarginMenuStart;
@@ -161,6 +165,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnCaptionTouchListener = onCaptionTouchListener;
}
+ void setCornersListener(TaskCornersListener cornersListener) {
+ mCornersListener = cornersListener;
+ }
+
void setDragPositioningCallback(DragPositioningCallback dragPositioningCallback) {
mDragPositioningCallback = dragPositioningCallback;
}
@@ -278,8 +286,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
.getDimensionPixelSize(R.dimen.freeform_resize_handle);
final int resize_corner = mResult.mRootView.getResources()
.getDimensionPixelSize(R.dimen.freeform_resize_corner);
- mDragResizeListener.setGeometry(
- mResult.mWidth, mResult.mHeight, resize_handle, resize_corner, touchSlop);
+
+ // If either task geometry or position have changed, update this task's cornersListener
+ if (mDragResizeListener.setGeometry(
+ mResult.mWidth, mResult.mHeight, resize_handle, resize_corner, touchSlop)
+ || !mTaskInfo.positionInParent.equals(mPositionInParent)) {
+ mCornersListener.onTaskCornersChanged(mTaskInfo.taskId, getGlobalCornersRegion());
+ }
+ mPositionInParent.set(mTaskInfo.positionInParent);
}
boolean isHandleMenuActive() {
@@ -586,6 +600,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
public void close() {
closeDragResizeListener();
closeHandleMenu();
+ mCornersListener.onTaskCornersRemoved(mTaskInfo.taskId);
super.close();
}
@@ -598,6 +613,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
: R.layout.desktop_mode_focused_window_decor;
}
+ /**
+ * Create a new region out of the corner rects of this task.
+ */
+ Region getGlobalCornersRegion() {
+ Region cornersRegion = mDragResizeListener.getCornersRegion();
+ cornersRegion.translate(mPositionInParent.x, mPositionInParent.y);
+ return cornersRegion;
+ }
+
static class Factory {
DesktopModeWindowDecoration create(
@@ -620,4 +644,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
syncQueue);
}
}
+
+ interface TaskCornersListener {
+ /** Inform the implementing class of this task's change in corner resize handles */
+ void onTaskCornersChanged(int taskId, Region corner);
+
+ /** Inform the implementing class that this task no longer needs its corners tracked,
+ * likely due to it closing. */
+ void onTaskCornersRemoved(int taskId);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index d5437c72acac..34c8c087cf95 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -134,13 +134,14 @@ class DragResizeInputListener implements AutoCloseable {
* @param cornerSize The size of the resize handle centered in each corner.
* @param touchSlop The distance in pixels user has to drag with touch for it to register as
* a resize action.
+ * @return whether the geometry has changed or not
*/
- void setGeometry(int taskWidth, int taskHeight, int resizeHandleThickness, int cornerSize,
+ boolean setGeometry(int taskWidth, int taskHeight, int resizeHandleThickness, int cornerSize,
int touchSlop) {
if (mTaskWidth == taskWidth && mTaskHeight == taskHeight
&& mResizeHandleThickness == resizeHandleThickness
&& mCornerSize == cornerSize) {
- return;
+ return false;
}
mTaskWidth = taskWidth;
@@ -220,6 +221,19 @@ class DragResizeInputListener implements AutoCloseable {
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
+ return true;
+ }
+
+ /**
+ * Generate a Region that encapsulates all 4 corner handles
+ */
+ Region getCornersRegion() {
+ Region region = new Region();
+ region.union(mLeftTopCornerBounds);
+ region.union(mLeftBottomCornerBounds);
+ region.union(mRightTopCornerBounds);
+ region.union(mRightBottomCornerBounds);
+ return region;
}
@Override
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
index da95c77d2b89..499870220190 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
@@ -48,9 +48,8 @@ public class TestShellExecutor implements ShellExecutor {
}
public void flushAll() {
- for (Runnable r : mRunnables) {
- r.run();
+ while (!mRunnables.isEmpty()) {
+ mRunnables.remove(0).run();
}
- mRunnables.clear();
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index b6d7ff3cd5cf..0a515cdea465 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -471,4 +471,53 @@ public class TaskViewTest extends ShellTestCase {
assertThat(insetsInfo.touchableRegion.contains(20, 20)).isFalse();
assertThat(insetsInfo.touchableRegion.contains(30, 30)).isFalse();
}
+
+ @Test
+ public void testTaskViewPrepareOpenAnimationSetsBoundsAndVisibility() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+ TaskViewBase taskViewBase = mock(TaskViewBase.class);
+ Rect bounds = new Rect(0, 0, 100, 100);
+ when(taskViewBase.getCurrentBoundsOnScreen()).thenReturn(bounds);
+ mTaskViewTaskController.setTaskViewBase(taskViewBase);
+
+ // Surface created, but task not available so bounds / visibility isn't set
+ mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+ verify(mTaskViewTransitions, never()).updateVisibilityState(
+ eq(mTaskViewTaskController), eq(true));
+
+ // Make the task available / start prepareOpen
+ WindowContainerTransaction wct = mock(WindowContainerTransaction.class);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
+
+ // Bounds got set
+ verify(wct).setBounds(any(WindowContainerToken.class), eq(bounds));
+ // Visibility & bounds state got set
+ verify(mTaskViewTransitions).updateVisibilityState(eq(mTaskViewTaskController), eq(true));
+ verify(mTaskViewTransitions).updateBoundsState(eq(mTaskViewTaskController), eq(bounds));
+ }
+
+ @Test
+ public void testTaskViewPrepareOpenAnimationSetsVisibilityFalse() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+ TaskViewBase taskViewBase = mock(TaskViewBase.class);
+ Rect bounds = new Rect(0, 0, 100, 100);
+ when(taskViewBase.getCurrentBoundsOnScreen()).thenReturn(bounds);
+ mTaskViewTaskController.setTaskViewBase(taskViewBase);
+
+ // Task is available, but the surface was never created
+ WindowContainerTransaction wct = mock(WindowContainerTransaction.class);
+ mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+ new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+ mLeash, wct);
+
+ // Bounds do not get set as there is no surface
+ verify(wct, never()).setBounds(any(WindowContainerToken.class), any());
+ // Visibility is set to false, bounds aren't set
+ verify(mTaskViewTransitions).updateVisibilityState(eq(mTaskViewTaskController), eq(false));
+ verify(mTaskViewTransitions, never()).updateBoundsState(eq(mTaskViewTaskController), any());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
new file mode 100644
index 000000000000..45590951dd1d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.wm.shell.taskview;
+
+import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.graphics.Rect;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.transition.Transitions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class TaskViewTransitionsTest extends ShellTestCase {
+
+ @Mock
+ Transitions mTransitions;
+ @Mock
+ TaskViewTaskController mTaskViewTaskController;
+ @Mock
+ ActivityManager.RunningTaskInfo mTaskInfo;
+ @Mock
+ WindowContainerToken mToken;
+
+ TaskViewTransitions mTaskViewTransitions;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ doReturn(true).when(mTransitions).isRegistered();
+ }
+
+ mTaskInfo = new ActivityManager.RunningTaskInfo();
+ mTaskInfo.token = mToken;
+ mTaskInfo.taskId = 314;
+ mTaskInfo.taskDescription = mock(ActivityManager.TaskDescription.class);
+
+ mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions));
+ mTaskViewTransitions.addTaskView(mTaskViewTaskController);
+ when(mTaskViewTaskController.getTaskInfo()).thenReturn(mTaskInfo);
+ }
+
+ @Test
+ public void testSetTaskBounds_taskNotVisible_noTransaction() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+ mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, false);
+ mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
+ new Rect(0, 0, 100, 100));
+
+ assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE))
+ .isNull();
+ }
+
+ @Test
+ public void testSetTaskBounds_taskVisible_boundsChangeTransaction() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+ mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true);
+
+ // Consume the pending transaction from visibility change
+ TaskViewTransitions.PendingTransition pending =
+ mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
+ assertThat(pending).isNotNull();
+ mTaskViewTransitions.startAnimation(pending.mClaimed,
+ mock(TransitionInfo.class),
+ new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(),
+ mock(Transitions.TransitionFinishCallback.class));
+ // Verify it was consumed
+ TaskViewTransitions.PendingTransition pending2 =
+ mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
+ assertThat(pending2).isNull();
+
+ // Test that set bounds creates a new transaction
+ mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
+ new Rect(0, 0, 100, 100));
+ assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE))
+ .isNotNull();
+ }
+
+ @Test
+ public void testSetTaskBounds_taskVisibleWithPending_noTransaction() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+ mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true);
+
+ TaskViewTransitions.PendingTransition pending =
+ mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
+ assertThat(pending).isNotNull();
+
+ mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
+ new Rect(0, 0, 100, 100));
+ assertThat(mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE))
+ .isNull();
+ }
+
+ @Test
+ public void testSetTaskBounds_sameBounds_noTransaction() {
+ assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
+
+ mTaskViewTransitions.setTaskViewVisible(mTaskViewTaskController, true);
+
+ // Consume the pending transaction from visibility change
+ TaskViewTransitions.PendingTransition pending =
+ mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
+ assertThat(pending).isNotNull();
+ mTaskViewTransitions.startAnimation(pending.mClaimed,
+ mock(TransitionInfo.class),
+ new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(),
+ mock(Transitions.TransitionFinishCallback.class));
+ // Verify it was consumed
+ TaskViewTransitions.PendingTransition pending2 =
+ mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_TO_FRONT);
+ assertThat(pending2).isNull();
+
+ // Test that set bounds creates a new transaction
+ mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
+ new Rect(0, 0, 100, 100));
+ TaskViewTransitions.PendingTransition pendingBounds =
+ mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE);
+ assertThat(pendingBounds).isNotNull();
+
+ // Consume the pending bounds transaction
+ mTaskViewTransitions.startAnimation(pendingBounds.mClaimed,
+ mock(TransitionInfo.class),
+ new SurfaceControl.Transaction(),
+ new SurfaceControl.Transaction(),
+ mock(Transitions.TransitionFinishCallback.class));
+ // Verify it was consumed
+ TaskViewTransitions.PendingTransition pendingBounds1 =
+ mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE);
+ assertThat(pendingBounds1).isNull();
+
+ // Test that setting the same bounds doesn't creates a new transaction
+ mTaskViewTransitions.setTaskBounds(mTaskViewTaskController,
+ new Rect(0, 0, 100, 100));
+ TaskViewTransitions.PendingTransition pendingBounds2 =
+ mTaskViewTransitions.findPending(mTaskViewTaskController, TRANSIT_CHANGE);
+ assertThat(pendingBounds2).isNull();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 5cd548bfe5ab..8eb5c6a08d88 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -30,10 +30,12 @@ import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
+import static android.window.TransitionInfo.FLAG_SYNC;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -63,6 +65,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.ArraySet;
+import android.util.Pair;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -587,7 +590,8 @@ public class ShellTransitionTests extends ShellTestCase {
requestStartTransition(transitions, transitTokenNotReady);
mDefaultHandler.setSimulateMerge(true);
- mDefaultHandler.mFinishes.get(0).onTransitionFinished(null /* wct */, null /* wctCB */);
+ mDefaultHandler.mFinishes.get(0).second.onTransitionFinished(
+ null /* wct */, null /* wctCB */);
// Make sure that the non-ready transition is not merged.
assertEquals(0, mDefaultHandler.mergeCount());
@@ -1059,6 +1063,223 @@ public class ShellTransitionTests extends ShellTestCase {
assertEquals(1, mDefaultHandler.activeCount());
}
+ @Test
+ public void testMultipleTracks() {
+ Transitions transitions = createTestTransitions();
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+ TestTransitionHandler alwaysMergeHandler = new TestTransitionHandler();
+ alwaysMergeHandler.setSimulateMerge(true);
+
+ final boolean[] becameIdle = new boolean[]{false};
+
+ final WindowContainerTransaction emptyWCT = new WindowContainerTransaction();
+ final SurfaceControl.Transaction mockSCT = mock(SurfaceControl.Transaction.class);
+
+ // Make this always merge so we can ensure that it does NOT get a merge-attempt for a
+ // different track.
+ IBinder transitA = transitions.startTransition(TRANSIT_OPEN, emptyWCT, alwaysMergeHandler);
+ // start tracking idle
+ transitions.runOnIdle(() -> becameIdle[0] = true);
+
+ IBinder transitB = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler);
+ IBinder transitC = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, mDefaultHandler);
+
+ TransitionInfo infoA = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).build();
+ infoA.setTrack(0);
+ TransitionInfo infoB = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).build();
+ infoB.setTrack(1);
+ TransitionInfo infoC = new TransitionInfoBuilder(TRANSIT_CLOSE)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ infoC.setTrack(1);
+
+ transitions.onTransitionReady(transitA, infoA, mockSCT, mockSCT);
+ assertEquals(1, alwaysMergeHandler.activeCount());
+ transitions.onTransitionReady(transitB, infoB, mockSCT, mockSCT);
+ // should now be running in parallel
+ assertEquals(1, mDefaultHandler.activeCount());
+ assertEquals(1, alwaysMergeHandler.activeCount());
+ // make sure we didn't try to merge into a different track.
+ assertEquals(0, alwaysMergeHandler.mergeCount());
+
+ // This should be queued-up since it is on track 1 (same as B)
+ transitions.onTransitionReady(transitC, infoC, mockSCT, mockSCT);
+ assertEquals(1, mDefaultHandler.activeCount());
+ assertEquals(1, alwaysMergeHandler.activeCount());
+
+ // Now finish B and make sure C starts
+ mDefaultHandler.finishOne();
+ mMainExecutor.flushAll();
+
+ // Now C and A running in parallel
+ assertEquals(1, mDefaultHandler.activeCount());
+ assertEquals(1, alwaysMergeHandler.activeCount());
+ assertEquals(0, alwaysMergeHandler.mergeCount());
+
+ // Finish A
+ alwaysMergeHandler.finishOne();
+ mMainExecutor.flushAll();
+
+ // C still running
+ assertEquals(0, alwaysMergeHandler.activeCount());
+ assertEquals(1, mDefaultHandler.activeCount());
+ assertFalse(becameIdle[0]);
+
+ mDefaultHandler.finishOne();
+ mMainExecutor.flushAll();
+
+ assertEquals(0, mDefaultHandler.activeCount());
+ assertTrue(becameIdle[0]);
+ }
+
+ @Test
+ public void testSyncMultipleTracks() {
+ Transitions transitions = createTestTransitions();
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+ TestTransitionHandler secondHandler = new TestTransitionHandler();
+
+ // Disable the forced early-sync-finish so that we can test the ordering mechanics.
+ transitions.setDisableForceSyncForTest(true);
+ mDefaultHandler.mFinishOnSync = false;
+ secondHandler.mFinishOnSync = false;
+
+ final WindowContainerTransaction emptyWCT = new WindowContainerTransaction();
+ final SurfaceControl.Transaction mockSCT = mock(SurfaceControl.Transaction.class);
+
+ // Make this always merge so we can ensure that it does NOT get a merge-attempt for a
+ // different track.
+ IBinder transitA = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler);
+ IBinder transitB = transitions.startTransition(TRANSIT_OPEN, emptyWCT, secondHandler);
+ IBinder transitC = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, secondHandler);
+ IBinder transitSync = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, mDefaultHandler);
+ IBinder transitD = transitions.startTransition(TRANSIT_OPEN, emptyWCT, secondHandler);
+ IBinder transitE = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler);
+
+ TransitionInfo infoA = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).build();
+ infoA.setTrack(0);
+ TransitionInfo infoB = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).build();
+ infoB.setTrack(1);
+ TransitionInfo infoC = new TransitionInfoBuilder(TRANSIT_CLOSE)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ infoC.setTrack(1);
+ TransitionInfo infoSync = new TransitionInfoBuilder(TRANSIT_CLOSE)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ infoSync.setTrack(0);
+ infoSync.setFlags(FLAG_SYNC);
+ TransitionInfo infoD = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).build();
+ infoD.setTrack(1);
+ TransitionInfo infoE = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).build();
+ infoE.setTrack(0);
+
+ // Start A B and C where A is track 0, B and C are track 1 (C should be queued)
+ transitions.onTransitionReady(transitA, infoA, mockSCT, mockSCT);
+ transitions.onTransitionReady(transitB, infoB, mockSCT, mockSCT);
+ transitions.onTransitionReady(transitC, infoC, mockSCT, mockSCT);
+ // should now be running in parallel (with one queued)
+ assertEquals(1, mDefaultHandler.activeCount());
+ assertEquals(1, secondHandler.activeCount());
+
+ // Make the sync ready and the following (D, E) ready.
+ transitions.onTransitionReady(transitSync, infoSync, mockSCT, mockSCT);
+ transitions.onTransitionReady(transitD, infoD, mockSCT, mockSCT);
+ transitions.onTransitionReady(transitE, infoE, mockSCT, mockSCT);
+
+ // nothing should have happened yet since the sync is queued and blocking everything.
+ assertEquals(1, mDefaultHandler.activeCount());
+ assertEquals(1, secondHandler.activeCount());
+
+ // Finish A (which is track 0 like the sync).
+ mDefaultHandler.finishOne();
+ mMainExecutor.flushAll();
+
+ // Even though the sync is on track 0 and track 0 became idle, it should NOT be started yet
+ // because it must wait for everything. Additionally, D/E shouldn't start yet either.
+ assertEquals(0, mDefaultHandler.activeCount());
+ assertEquals(1, secondHandler.activeCount());
+
+ // Now finish B and C -- this should then allow the sync to start and D to run (in parallel)
+ secondHandler.finishOne();
+ secondHandler.finishOne();
+ mMainExecutor.flushAll();
+
+ // Now the sync and D (on track 1) should be running
+ assertEquals(1, mDefaultHandler.activeCount());
+ assertEquals(1, secondHandler.activeCount());
+
+ // finish the sync. track 0 still has E
+ mDefaultHandler.finishOne();
+ mMainExecutor.flushAll();
+ assertEquals(1, mDefaultHandler.activeCount());
+
+ mDefaultHandler.finishOne();
+ secondHandler.finishOne();
+ mMainExecutor.flushAll();
+
+ assertEquals(0, mDefaultHandler.activeCount());
+ assertEquals(0, secondHandler.activeCount());
+ }
+
+ @Test
+ public void testForceSyncTracks() {
+ Transitions transitions = createTestTransitions();
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+ TestTransitionHandler secondHandler = new TestTransitionHandler();
+
+ final WindowContainerTransaction emptyWCT = new WindowContainerTransaction();
+ final SurfaceControl.Transaction mockSCT = mock(SurfaceControl.Transaction.class);
+
+ // Make this always merge so we can ensure that it does NOT get a merge-attempt for a
+ // different track.
+ IBinder transitA = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler);
+ IBinder transitB = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler);
+ IBinder transitC = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, secondHandler);
+ IBinder transitD = transitions.startTransition(TRANSIT_OPEN, emptyWCT, secondHandler);
+ IBinder transitSync = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, mDefaultHandler);
+
+ TransitionInfo infoA = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).build();
+ infoA.setTrack(0);
+ TransitionInfo infoB = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).build();
+ infoB.setTrack(0);
+ TransitionInfo infoC = new TransitionInfoBuilder(TRANSIT_CLOSE)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ infoC.setTrack(1);
+ TransitionInfo infoD = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN).build();
+ infoD.setTrack(1);
+ TransitionInfo infoSync = new TransitionInfoBuilder(TRANSIT_CLOSE)
+ .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+ infoSync.setTrack(0);
+ infoSync.setFlags(FLAG_SYNC);
+
+ transitions.onTransitionReady(transitA, infoA, mockSCT, mockSCT);
+ transitions.onTransitionReady(transitB, infoB, mockSCT, mockSCT);
+ transitions.onTransitionReady(transitC, infoC, mockSCT, mockSCT);
+ transitions.onTransitionReady(transitD, infoD, mockSCT, mockSCT);
+ // should now be running in parallel (with one queued in each)
+ assertEquals(1, mDefaultHandler.activeCount());
+ assertEquals(1, secondHandler.activeCount());
+
+ // Make the sync ready.
+ transitions.onTransitionReady(transitSync, infoSync, mockSCT, mockSCT);
+ mMainExecutor.flushAll();
+
+ // Everything should be forced-finish now except the sync
+ assertEquals(1, mDefaultHandler.activeCount());
+ assertEquals(0, secondHandler.activeCount());
+
+ mDefaultHandler.finishOne();
+ mMainExecutor.flushAll();
+
+ assertEquals(0, mDefaultHandler.activeCount());
+ }
+
class ChangeBuilder {
final TransitionInfo.Change mChange;
@@ -1097,9 +1318,11 @@ public class ShellTransitionTests extends ShellTestCase {
}
class TestTransitionHandler implements Transitions.TransitionHandler {
- ArrayList<Transitions.TransitionFinishCallback> mFinishes = new ArrayList<>();
+ ArrayList<Pair<IBinder, Transitions.TransitionFinishCallback>> mFinishes =
+ new ArrayList<>();
final ArrayList<IBinder> mMerged = new ArrayList<>();
boolean mSimulateMerge = false;
+ boolean mFinishOnSync = true;
final ArraySet<IBinder> mShouldMerge = new ArraySet<>();
@Override
@@ -1107,7 +1330,7 @@ public class ShellTransitionTests extends ShellTestCase {
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- mFinishes.add(finishCallback);
+ mFinishes.add(new Pair<>(transition, finishCallback));
return true;
}
@@ -1115,6 +1338,13 @@ public class ShellTransitionTests extends ShellTestCase {
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (mFinishOnSync && info.getType() == TRANSIT_SLEEP) {
+ for (int i = 0; i < mFinishes.size(); ++i) {
+ if (mFinishes.get(i).first != mergeTarget) continue;
+ mFinishes.remove(i).second.onTransitionFinished(null, null);
+ return;
+ }
+ }
if (!(mSimulateMerge || mShouldMerge.contains(transition))) return;
mMerged.add(transition);
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
@@ -1136,18 +1366,19 @@ public class ShellTransitionTests extends ShellTestCase {
}
void finishAll() {
- final ArrayList<Transitions.TransitionFinishCallback> finishes = mFinishes;
+ final ArrayList<Pair<IBinder, Transitions.TransitionFinishCallback>> finishes =
+ mFinishes;
mFinishes = new ArrayList<>();
for (int i = finishes.size() - 1; i >= 0; --i) {
- finishes.get(i).onTransitionFinished(null /* wct */, null /* wctCB */);
+ finishes.get(i).second.onTransitionFinished(null /* wct */, null /* wctCB */);
}
mShouldMerge.clear();
}
void finishOne() {
- Transitions.TransitionFinishCallback fin = mFinishes.remove(0);
+ Pair<IBinder, Transitions.TransitionFinishCallback> fin = mFinishes.remove(0);
mMerged.clear();
- fin.onTransitionFinished(null /* wct */, null /* wctCB */);
+ fin.second.onTransitionFinished(null /* wct */, null /* wctCB */);
}
int activeCount() {
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 9db47c3ba090..a8d170d00ef7 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -207,27 +207,6 @@ static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
}
}
-static void computeTransformImpl(const DirtyStack* frame, const DirtyStack* end,
- Matrix4* outMatrix) {
- while (frame != end) {
- switch (frame->type) {
- case TransformRenderNode:
- frame->renderNode->applyViewPropertyTransforms(*outMatrix);
- break;
- case TransformMatrix4:
- outMatrix->multiply(*frame->matrix4);
- break;
- case TransformNone:
- // nothing to be done
- break;
- default:
- LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d",
- frame->type);
- }
- frame = frame->prev;
- }
-}
-
void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
if (frame->pendingDirty.isEmpty()) {
return;
@@ -282,9 +261,6 @@ void DamageAccumulator::finish(SkRect* totalDirty) {
DamageAccumulator::StretchResult DamageAccumulator::findNearestStretchEffect() const {
DirtyStack* frame = mHead;
- const auto& headProperties = mHead->renderNode->properties();
- float startWidth = headProperties.getWidth();
- float startHeight = headProperties.getHeight();
while (frame->prev != frame) {
if (frame->type == TransformRenderNode) {
const auto& renderNode = frame->renderNode;
@@ -295,21 +271,16 @@ DamageAccumulator::StretchResult DamageAccumulator::findNearestStretchEffect() c
const float height = (float) frameRenderNodeProperties.getHeight();
if (!effect.isEmpty()) {
Matrix4 stretchMatrix;
- computeTransformImpl(mHead, frame, &stretchMatrix);
- Rect stretchRect = Rect(0.f, 0.f, startWidth, startHeight);
+ computeTransformImpl(frame, &stretchMatrix);
+ Rect stretchRect = Rect(0.f, 0.f, width, height);
stretchMatrix.mapRect(stretchRect);
return StretchResult{
- .stretchEffect = &effect,
- .childRelativeBounds = SkRect::MakeLTRB(
- stretchRect.left,
- stretchRect.top,
- stretchRect.right,
- stretchRect.bottom
- ),
- .width = width,
- .height = height
- };
+ .stretchEffect = &effect,
+ .parentBounds = SkRect::MakeLTRB(stretchRect.left, stretchRect.top,
+ stretchRect.right, stretchRect.bottom),
+ .width = width,
+ .height = height};
}
}
frame = frame->prev;
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index 90a35174d929..c4249af392d3 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -70,9 +70,9 @@ public:
const StretchEffect* stretchEffect;
/**
- * Bounds of the child relative to the stretch container
+ * Bounds of the stretching container
*/
- const SkRect childRelativeBounds;
+ const SkRect parentBounds;
/**
* Width of the stretch container
diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp
index c57e6f09347a..38d17de166e9 100644
--- a/libs/hwui/jni/BitmapFactory.cpp
+++ b/libs/hwui/jni/BitmapFactory.cpp
@@ -401,6 +401,14 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
decodeColorType = kN32_SkColorType;
}
+ // b/276879147, fallback to RGBA_8888 when decoding HEIF and P010 is not supported.
+ if (decodeColorType == kRGBA_1010102_SkColorType &&
+ codec->getEncodedFormat() == SkEncodedImageFormat::kHEIF &&
+ env->CallStaticBooleanMethod(gImageDecoder_class,
+ gImageDecoder_isP010SupportedForHEVCMethodID) == JNI_FALSE) {
+ decodeColorType = kN32_SkColorType;
+ }
+
sk_sp<SkColorSpace> decodeColorSpace = codec->computeOutputColorSpace(
decodeColorType, prefColorSpace);
diff --git a/libs/hwui/jni/BitmapFactory.h b/libs/hwui/jni/BitmapFactory.h
index 45bffc44967d..a079cb4b513d 100644
--- a/libs/hwui/jni/BitmapFactory.h
+++ b/libs/hwui/jni/BitmapFactory.h
@@ -1,8 +1,9 @@
#ifndef _ANDROID_GRAPHICS_BITMAP_FACTORY_H_
#define _ANDROID_GRAPHICS_BITMAP_FACTORY_H_
+#include <SkEncodedImageFormat.h>
+
#include "GraphicsJNI.h"
-#include "SkEncodedImageFormat.h"
extern jclass gOptions_class;
extern jfieldID gOptions_justBoundsFieldID;
@@ -26,6 +27,9 @@ extern jfieldID gOptions_bitmapFieldID;
extern jclass gBitmapConfig_class;
extern jmethodID gBitmapConfig_nativeToConfigMethodID;
+extern jclass gImageDecoder_class;
+extern jmethodID gImageDecoder_isP010SupportedForHEVCMethodID;
+
jstring getMimeTypeAsJavaString(JNIEnv*, SkEncodedImageFormat);
#endif // _ANDROID_GRAPHICS_BITMAP_FACTORY_H_
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
index db1c188e425e..6744c6c0b9ec 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -25,6 +25,7 @@
#include <SkCodecAnimation.h>
#include <SkColorSpace.h>
#include <SkColorType.h>
+#include <SkEncodedImageFormat.h>
#include <SkImageInfo.h>
#include <SkRect.h>
#include <SkSize.h>
@@ -48,7 +49,8 @@
using namespace android;
-static jclass gImageDecoder_class;
+jclass gImageDecoder_class;
+jmethodID gImageDecoder_isP010SupportedForHEVCMethodID;
static jclass gSize_class;
static jclass gDecodeException_class;
static jclass gCanvas_class;
@@ -298,6 +300,14 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
colorType = kN32_SkColorType;
}
+ // b/276879147, fallback to RGBA_8888 when decoding HEIF and P010 is not supported.
+ if (colorType == kRGBA_1010102_SkColorType &&
+ decoder->mCodec->getEncodedFormat() == SkEncodedImageFormat::kHEIF &&
+ env->CallStaticBooleanMethod(gImageDecoder_class,
+ gImageDecoder_isP010SupportedForHEVCMethodID) == JNI_FALSE) {
+ colorType = kN32_SkColorType;
+ }
+
if (!decoder->setOutColorType(colorType)) {
doThrowISE(env, "Failed to set out color type!");
return nullptr;
@@ -540,6 +550,8 @@ int register_android_graphics_ImageDecoder(JNIEnv* env) {
gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder"));
gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JIIZZ)V");
gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;)I");
+ gImageDecoder_isP010SupportedForHEVCMethodID =
+ GetStaticMethodIDOrDie(env, gImageDecoder_class, "isP010SupportedForHEVC", "()Z");
gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size"));
gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V");
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index ac1f92dee507..24a785c18711 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -584,13 +584,15 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
uirenderer::Rect bounds(props.getWidth(), props.getHeight());
bool useStretchShader =
Properties::getStretchEffectBehavior() != StretchEffectBehavior::UniformScale;
- if (useStretchShader && info.stretchEffectCount) {
+ // Compute the transform bounds first before calculating the stretch
+ transform.mapRect(bounds);
+
+ bool hasStretch = useStretchShader && info.stretchEffectCount;
+ if (hasStretch) {
handleStretchEffect(info, bounds);
}
- transform.mapRect(bounds);
-
- if (CC_LIKELY(transform.isPureTranslate())) {
+ if (CC_LIKELY(transform.isPureTranslate()) && !hasStretch) {
// snap/round the computed bounds, so they match the rounding behavior
// of the clear done in SurfaceView#draw().
bounds.snapGeometryToPixelBoundaries(false);
@@ -665,45 +667,42 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
return env;
}
- void stretchTargetBounds(const StretchEffect& stretchEffect,
- float width, float height,
- const SkRect& childRelativeBounds,
- uirenderer::Rect& bounds) {
- float normalizedLeft = childRelativeBounds.left() / width;
- float normalizedTop = childRelativeBounds.top() / height;
- float normalizedRight = childRelativeBounds.right() / width;
- float normalizedBottom = childRelativeBounds.bottom() / height;
- float reverseLeft = width *
- (stretchEffect.computeStretchedPositionX(normalizedLeft) -
- normalizedLeft);
- float reverseTop = height *
- (stretchEffect.computeStretchedPositionY(normalizedTop) -
- normalizedTop);
- float reverseRight = width *
- (stretchEffect.computeStretchedPositionX(normalizedRight) -
- normalizedLeft);
- float reverseBottom = height *
- (stretchEffect.computeStretchedPositionY(normalizedBottom) -
- normalizedTop);
- bounds.left = reverseLeft;
- bounds.top = reverseTop;
- bounds.right = reverseRight;
- bounds.bottom = reverseBottom;
- }
-
void handleStretchEffect(const TreeInfo& info, uirenderer::Rect& targetBounds) {
// Search up to find the nearest stretcheffect parent
const DamageAccumulator::StretchResult result =
info.damageAccumulator->findNearestStretchEffect();
const StretchEffect* effect = result.stretchEffect;
- if (!effect) {
+ if (effect) {
+ // Compute the number of pixels that the stretching container
+ // scales by.
+ // Then compute the scale factor that the child would need
+ // to scale in order to occupy the same pixel bounds.
+ auto& parentBounds = result.parentBounds;
+ auto parentWidth = parentBounds.width();
+ auto parentHeight = parentBounds.height();
+ auto& stretchDirection = effect->getStretchDirection();
+ auto stretchX = stretchDirection.x();
+ auto stretchY = stretchDirection.y();
+ auto stretchXPixels = parentWidth * std::abs(stretchX);
+ auto stretchYPixels = parentHeight * std::abs(stretchY);
+ SkMatrix stretchMatrix;
+
+ auto childScaleX = 1 + (stretchXPixels / targetBounds.getWidth());
+ auto childScaleY = 1 + (stretchYPixels / targetBounds.getHeight());
+ auto pivotX = stretchX > 0 ? targetBounds.left : targetBounds.right;
+ auto pivotY = stretchY > 0 ? targetBounds.top : targetBounds.bottom;
+ stretchMatrix.setScale(childScaleX, childScaleY, pivotX, pivotY);
+ SkRect rect = SkRect::MakeLTRB(targetBounds.left, targetBounds.top,
+ targetBounds.right, targetBounds.bottom);
+ SkRect dst = stretchMatrix.mapRect(rect);
+ targetBounds.left = dst.left();
+ targetBounds.top = dst.top();
+ targetBounds.right = dst.right();
+ targetBounds.bottom = dst.bottom();
+ } else {
return;
}
- const auto& childRelativeBounds = result.childRelativeBounds;
- stretchTargetBounds(*effect, result.width, result.height,
- childRelativeBounds,targetBounds);
-
if (Properties::getStretchEffectBehavior() ==
StretchEffectBehavior::Shader) {
JNIEnv* env = jnienv();
@@ -714,9 +713,8 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
gPositionListener.clazz, gPositionListener.callApplyStretch, mListener,
info.canvasContext.getFrameNumber(), result.width, result.height,
stretchDirection.fX, stretchDirection.fY, effect->maxStretchAmountX,
- effect->maxStretchAmountY, childRelativeBounds.left(),
- childRelativeBounds.top(), childRelativeBounds.right(),
- childRelativeBounds.bottom());
+ effect->maxStretchAmountY, targetBounds.left, targetBounds.top,
+ targetBounds.right, targetBounds.bottom);
if (!keepListening) {
env->DeleteGlobalRef(mListener);
mListener = nullptr;
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index 7ac84460463e..f4caad727407 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -20,6 +20,7 @@ import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.hardware.HardwareBuffer;
import android.media.Image.Plane;
+import android.util.Log;
import android.util.Size;
import libcore.io.Memory;
@@ -30,6 +31,7 @@ import java.nio.ByteBuffer;
* Package private utility class for hosting commonly used Image related methods.
*/
class ImageUtils {
+ private static final String IMAGEUTILS_LOG_TAG = "ImageUtils";
/**
* Only a subset of the formats defined in
@@ -266,11 +268,15 @@ class ImageUtils {
break;
case PixelFormat.RGBA_8888:
case PixelFormat.RGBX_8888:
+ case PixelFormat.RGBA_1010102:
estimatedBytePerPixel = 4.0;
break;
default:
- throw new UnsupportedOperationException(
- String.format("Invalid format specified %d", format));
+ if (Log.isLoggable(IMAGEUTILS_LOG_TAG, Log.VERBOSE)) {
+ Log.v(IMAGEUTILS_LOG_TAG, "getEstimatedNativeAllocBytes() uses default"
+ + "estimated native allocation size.");
+ }
+ estimatedBytePerPixel = 1.0;
}
return (int)(width * height * estimatedBytePerPixel * numImages);
@@ -295,6 +301,7 @@ class ImageUtils {
}
case PixelFormat.RGB_565:
case PixelFormat.RGBA_8888:
+ case PixelFormat.RGBA_1010102:
case PixelFormat.RGBX_8888:
case PixelFormat.RGB_888:
case ImageFormat.JPEG:
@@ -312,8 +319,11 @@ class ImageUtils {
case ImageFormat.PRIVATE:
return new Size(0, 0);
default:
- throw new UnsupportedOperationException(
- String.format("Invalid image format %d", image.getFormat()));
+ if (Log.isLoggable(IMAGEUTILS_LOG_TAG, Log.VERBOSE)) {
+ Log.v(IMAGEUTILS_LOG_TAG, "getEffectivePlaneSizeForImage() uses"
+ + "image's width and height for plane size.");
+ }
+ return new Size(image.getWidth(), image.getHeight());
}
}
diff --git a/media/java/android/media/VolumeProvider.java b/media/java/android/media/VolumeProvider.java
index 7cf63f4d5aec..22741f450c18 100644
--- a/media/java/android/media/VolumeProvider.java
+++ b/media/java/android/media/VolumeProvider.java
@@ -17,6 +17,7 @@ package android.media;
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.media.MediaRouter2.RoutingController;
import android.media.session.MediaSession;
import java.lang.annotation.Retention;
@@ -66,32 +67,28 @@ public abstract class VolumeProvider {
private Callback mCallback;
/**
- * Create a new volume provider for handling volume events. You must specify
- * the type of volume control, the maximum volume that can be used, and the
- * current volume on the output.
+ * Creates a new volume provider for handling volume events.
*
- * @param volumeControl The method for controlling volume that is used by
- * this provider.
+ * @param volumeControl See {@link #getVolumeControl()}.
* @param maxVolume The maximum allowed volume.
* @param currentVolume The current volume on the output.
*/
-
public VolumeProvider(@ControlType int volumeControl, int maxVolume, int currentVolume) {
this(volumeControl, maxVolume, currentVolume, null);
}
/**
- * Create a new volume provider for handling volume events. You must specify
- * the type of volume control, the maximum volume that can be used, and the
- * current volume on the output.
+ * Creates a new volume provider for handling volume events.
*
- * @param volumeControl The method for controlling volume that is used by
- * this provider.
+ * @param volumeControl See {@link #getVolumeControl()}.
* @param maxVolume The maximum allowed volume.
* @param currentVolume The current volume on the output.
- * @param volumeControlId The volume control ID of this provider.
+ * @param volumeControlId See {@link #getVolumeControlId()}.
*/
- public VolumeProvider(@ControlType int volumeControl, int maxVolume, int currentVolume,
+ public VolumeProvider(
+ @ControlType int volumeControl,
+ int maxVolume,
+ int currentVolume,
@Nullable String volumeControlId) {
mControlType = volumeControl;
mMaxVolume = maxVolume;
@@ -100,7 +97,10 @@ public abstract class VolumeProvider {
}
/**
- * Get the volume control type that this volume provider uses.
+ * Gets the volume control type that this volume provider uses.
+ *
+ * <p>One of {@link #VOLUME_CONTROL_FIXED}, {@link #VOLUME_CONTROL_ABSOLUTE}, or {@link
+ * #VOLUME_CONTROL_RELATIVE}.
*
* @return The volume control type for this volume provider
*/
@@ -110,7 +110,7 @@ public abstract class VolumeProvider {
}
/**
- * Get the maximum volume this provider allows.
+ * Gets the maximum volume this provider allows.
*
* @return The max allowed volume.
*/
@@ -129,8 +129,8 @@ public abstract class VolumeProvider {
}
/**
- * Notify the system that the current volume has been changed. This must be
- * called every time the volume changes to ensure it is displayed properly.
+ * Notifies the system that the current volume has been changed. This must be called every time
+ * the volume changes to ensure it is displayed properly.
*
* @param currentVolume The current volume on the output.
*/
@@ -142,10 +142,11 @@ public abstract class VolumeProvider {
}
/**
- * Gets the volume control ID. It can be used to identify which volume provider is
- * used by the session.
+ * Gets the {@link RoutingController#getId() routing controller id} of the {@link
+ * RoutingController} associated with this volume provider, or null if unset.
*
- * @return the volume control ID or {@code null} if it isn't set.
+ * <p>This id allows mapping this volume provider to a routing controller, which provides
+ * information about the media route and allows controlling its volume.
*/
@Nullable
public final String getVolumeControlId() {
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 62c4a5167316..b43ff63f3fcc 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -29,7 +29,6 @@ import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
-import android.media.RoutingSessionInfo;
import android.media.VolumeProvider;
import android.media.VolumeProvider.ControlType;
import android.media.session.MediaSession.QueueItem;
@@ -993,26 +992,23 @@ public final class MediaController {
/**
* Creates a new playback info.
*
- * @param playbackType The playback type. Should be {@link #PLAYBACK_TYPE_LOCAL} or
- * {@link #PLAYBACK_TYPE_REMOTE}
- * @param volumeControl The volume control. Should be one of:
- * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE},
- * {@link VolumeProvider#VOLUME_CONTROL_RELATIVE}, and
- * {@link VolumeProvider#VOLUME_CONTROL_FIXED}.
+ * @param playbackType The playback type. Should be {@link #PLAYBACK_TYPE_LOCAL} or {@link
+ * #PLAYBACK_TYPE_REMOTE}
+ * @param volumeControl See {@link #getVolumeControl()}.
* @param maxVolume The max volume. Should be equal or greater than zero.
* @param currentVolume The current volume. Should be in the interval [0, maxVolume].
* @param audioAttrs The audio attributes for this playback. Should not be null.
- * @param volumeControlId The {@link RoutingSessionInfo#getId() routing session id} of the
- * {@link RoutingSessionInfo} associated with this controller, or null if not
- * applicable. This id allows mapping this controller to a routing session which, when
- * applicable, provides information about the remote device, and support for volume
- * adjustment.
+ * @param volumeControlId See {@link #getVolumeControlId()}.
* @hide
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public PlaybackInfo(@PlaybackType int playbackType, @ControlType int volumeControl,
- @IntRange(from = 0) int maxVolume, @IntRange(from = 0) int currentVolume,
- @NonNull AudioAttributes audioAttrs, @Nullable String volumeControlId) {
+ public PlaybackInfo(
+ @PlaybackType int playbackType,
+ @ControlType int volumeControl,
+ @IntRange(from = 0) int maxVolume,
+ @IntRange(from = 0) int currentVolume,
+ @NonNull AudioAttributes audioAttrs,
+ @Nullable String volumeControlId) {
mPlaybackType = playbackType;
mVolumeControl = volumeControl;
mMaxVolume = maxVolume;
@@ -1044,14 +1040,8 @@ public final class MediaController {
}
/**
- * Get the type of volume control that can be used. One of:
- * <ul>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
- * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
- * </ul>
- *
- * @return The type of volume control that may be used with this session.
+ * Get the volume control type associated to the session, as indicated by {@link
+ * VolumeProvider#getVolumeControl()}.
*/
public int getVolumeControl() {
return mVolumeControl;
@@ -1076,10 +1066,9 @@ public final class MediaController {
}
/**
- * Get the audio attributes for this session. The attributes will affect
- * volume handling for the session. When the volume type is
- * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
- * remote volume handler.
+ * Get the audio attributes for this session. The attributes will affect volume handling for
+ * the session. When the playback type is {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these
+ * may be ignored by the remote volume handler.
*
* @return The attributes for this session.
*/
@@ -1088,19 +1077,9 @@ public final class MediaController {
}
/**
- * Gets the volume control ID for this session. It can be used to identify which
- * volume provider is used by the session.
- * <p>
- * When the session starts to use {@link #PLAYBACK_TYPE_REMOTE remote volume handling},
- * a volume provider should be set and it may set the volume control ID of the provider
- * if the session wants to inform which volume provider is used.
- * It can be {@code null} if the session didn't set the volume control ID or it uses
- * {@link #PLAYBACK_TYPE_LOCAL local playback}.
- * </p>
- *
- * @return the volume control ID for this session or {@code null} if it uses local playback
- * or not set.
- * @see VolumeProvider#getVolumeControlId()
+ * Get the routing controller ID for this session, as indicated by {@link
+ * VolumeProvider#getVolumeControlId()}. Returns null if unset, or if {@link
+ * #getPlaybackType()} is {@link #PLAYBACK_TYPE_LOCAL}.
*/
@Nullable
public String getVolumeControlId() {
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index c41bd1bc3094..b6d70afa85b7 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -22,6 +22,7 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -56,6 +57,7 @@ import com.android.internal.app.ISoundTriggerSession;
import com.android.internal.util.Preconditions;
import java.util.HashMap;
+import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executor;
@@ -74,6 +76,7 @@ public final class SoundTriggerManager {
private static final String TAG = "SoundTriggerManager";
private final Context mContext;
+ private final ISoundTriggerService mSoundTriggerService;
private final ISoundTriggerSession mSoundTriggerSession;
private final IBinder mBinderToken = new Binder();
@@ -114,13 +117,97 @@ public final class SoundTriggerManager {
}
}
} catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
+ throw e.rethrowFromSystemServer();
}
mContext = context;
+ mSoundTriggerService = soundTriggerService;
mReceiverInstanceMap = new HashMap<UUID, SoundTriggerDetector>();
}
/**
+ * Construct a {@link SoundTriggerManager} which connects to a specified module.
+ *
+ * @param moduleProperties - Properties representing the module to attach to
+ * @return - A new {@link SoundTriggerManager} which interfaces with the test module.
+ * @hide
+ */
+ @TestApi
+ @SuppressLint("ManagerLookup")
+ public @NonNull SoundTriggerManager createManagerForModule(
+ @NonNull ModuleProperties moduleProperties) {
+ return new SoundTriggerManager(mContext, mSoundTriggerService,
+ Objects.requireNonNull(moduleProperties));
+ }
+
+ /**
+ * Construct a {@link SoundTriggerManager} which connects to a ST module
+ * which is available for instrumentation through {@link attachInstrumentation}.
+ *
+ * @return - A new {@link SoundTriggerManager} which interfaces with the test module.
+ * @hide
+ */
+ @TestApi
+ @SuppressLint("ManagerLookup")
+ public @NonNull SoundTriggerManager createManagerForTestModule() {
+ return new SoundTriggerManager(mContext, mSoundTriggerService, getTestModuleProperties());
+ }
+
+ private final @NonNull SoundTrigger.ModuleProperties getTestModuleProperties() {
+ var moduleProps = listModuleProperties()
+ .stream()
+ .filter((SoundTrigger.ModuleProperties prop)
+ -> prop.getSupportedModelArch().equals(SoundTrigger.FAKE_HAL_ARCH))
+ .findFirst()
+ .orElse(null);
+ if (moduleProps == null) {
+ throw new IllegalStateException("Fake ST HAL should always be available");
+ }
+ return moduleProps;
+ }
+
+ // Helper constructor to create a manager object attached to a specific ST module.
+ private SoundTriggerManager(@NonNull Context context,
+ @NonNull ISoundTriggerService soundTriggerService,
+ @NonNull ModuleProperties properties) {
+ try {
+ Identity originatorIdentity = new Identity();
+ originatorIdentity.packageName = ActivityThread.currentOpPackageName();
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ mSoundTriggerSession = soundTriggerService.attachAsOriginator(
+ originatorIdentity,
+ Objects.requireNonNull(properties),
+ mBinderToken);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ mContext = Objects.requireNonNull(context);
+ mSoundTriggerService = Objects.requireNonNull(soundTriggerService);
+ mReceiverInstanceMap = new HashMap<UUID, SoundTriggerDetector>();
+ }
+
+ /**
+ * Enumerate the available ST modules. Use {@link createManagerForModule(ModuleProperties)} to
+ * receive a {@link SoundTriggerManager} attached to a specified ST module.
+ * @return - List of available ST modules to attach to.
+ * @hide
+ */
+ @TestApi
+ public static @NonNull List<ModuleProperties> listModuleProperties() {
+ try {
+ ISoundTriggerService service = ISoundTriggerService.Stub.asInterface(
+ ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
+ Identity originatorIdentity = new Identity();
+ originatorIdentity.packageName = ActivityThread.currentOpPackageName();
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ return service.listModuleProperties(originatorIdentity);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Updates the given sound trigger model.
* @deprecated replace with {@link #loadSoundModel}
* SoundTriggerService model database will be removed
@@ -317,6 +404,17 @@ public final class SoundTriggerManager {
SoundTrigger.GenericSoundModel getGenericSoundModel() {
return mGenericSoundModel;
}
+
+ /**
+ * Return a {@link SoundTrigger.SoundModel} view of the model for
+ * test purposes.
+ * @hide
+ */
+ @TestApi
+ public @NonNull SoundTrigger.SoundModel getSoundModel() {
+ return mGenericSoundModel;
+ }
+
}
@@ -369,7 +467,8 @@ public final class SoundTriggerManager {
*/
@RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
@UnsupportedAppUsage
- public int loadSoundModel(SoundModel soundModel) {
+ @TestApi
+ public int loadSoundModel(@NonNull SoundModel soundModel) {
if (soundModel == null || mSoundTriggerSession == null) {
return STATUS_ERROR;
}
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
index d470d4c7b3f6..aae30dfe6223 100644
--- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -16,13 +16,15 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- style="@style/ScrollViewStyle">
+ style="@style/ScrollViewStyle"
+ android:importantForAccessibility="no">
<LinearLayout
android:id="@+id/activity_confirmation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:baselineAligned="false">
+ android:baselineAligned="false"
+ android:importantForAccessibility="no">
<LinearLayout android:id="@+id/association_confirmation"
style="@style/ContainerLayout">
@@ -153,7 +155,8 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_weight="1">
+ android:layout_weight="1"
+ android:importantForAccessibility="noHideDescendants">
<ProgressBar
android:id="@+id/spinner_single_device"
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
index 6c463e18ef11..6bfcd82b0a4a 100644
--- a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
+++ b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
@@ -22,7 +22,8 @@
android:orientation="horizontal"
android:paddingStart="32dp"
android:paddingEnd="32dp"
- android:paddingTop="12dp">
+ android:paddingTop="6dp"
+ android:paddingBottom="6dp">
<ImageView
android:id="@+id/permission_icon"
diff --git a/packages/CompanionDeviceManager/res/layout/vendor_header.xml b/packages/CompanionDeviceManager/res/layout/vendor_header.xml
index 16a87e589b8e..77c79b384276 100644
--- a/packages/CompanionDeviceManager/res/layout/vendor_header.xml
+++ b/packages/CompanionDeviceManager/res/layout/vendor_header.xml
@@ -33,7 +33,7 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="12dp"
- android:contentDescription="@string/vendor_icon_description" />
+ android:importantForAccessibility="no" />
<TextView
android:id="@+id/vendor_header_name"
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index ebfb86d4d6ba..74072e9d4ec3 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -113,6 +113,18 @@
<!-- Back button for the helper consent dialog [CHAR LIMIT=30] -->
<string name="consent_back">Back</string>
+ <!-- Action when permission list view is expanded CHAR LIMIT=30] -->
+ <string name="permission_expanded">Expanded</string>
+
+ <!-- Expand action permission list CHAR LIMIT=30] -->
+ <string name="permission_expand">Expand</string>
+
+ <!-- Action when permission list view is collapsed CHAR LIMIT=30] -->
+ <string name="permission_collapsed">Collapsed</string>
+
+ <!-- Collapse action permission list CHAR LIMIT=30] -->
+ <string name="permission_collapse">Collapse</string>
+
<!-- ================== System data transfer ==================== -->
<!-- Title of the permission sync confirmation dialog. [CHAR LIMIT=NONE] -->
<string name="permission_sync_confirmation_title">Give apps on &lt;strong&gt;<xliff:g id="companion_device_name" example="Galaxy Watch 5">%1$s</xliff:g>&lt;/strong&gt; the same permissions as on &lt;strong&gt;<xliff:g id="primary_device_name" example="Pixel 6">%2$s</xliff:g>&lt;/strong&gt;?</string>
@@ -120,11 +132,8 @@
<!-- Text of the permission sync explanation in the confirmation dialog. [CHAR LIMIT=NONE] -->
<string name="permission_sync_summary">This may include &lt;strong&gt;Microphone&lt;/strong&gt;, &lt;strong&gt;Camera&lt;/strong&gt;, and &lt;strong&gt;Location access&lt;/strong&gt;, and other sensitive permissions on &lt;strong&gt;<xliff:g id="companion_device_name" example="My Watch">%1$s</xliff:g>&lt;/strong&gt;. &lt;br/&gt;&lt;br/&gt;You can change these permissions any time in your Settings on &lt;strong&gt;<xliff:g id="companion_device_name" example="My Watch">%1$s</xliff:g>&lt;/strong&gt;.</string>
- <!--Description for vendor icon [CHAR LIMIT=30]-->
- <string name="vendor_icon_description">App Icon</string>
-
<!--Description for information icon [CHAR LIMIT=30]-->
- <string name="vendor_header_button_description">More Information Button</string>
+ <string name="vendor_header_button_description">More Information</string>
<!-- ================= Permissions ================= -->
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index b167377eabf7..e85190be0e1e 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -36,7 +36,7 @@
</style>
<style name="DescriptionTitle"
- parent="@android:style/TextAppearance.DeviceDefault.Medium">
+ parent="@android:style/TextAppearance.DeviceDefault">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:gravity">center</item>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
index d2fd78012193..b86ef649331a 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/PermissionListAdapter.java
@@ -27,6 +27,7 @@ import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
@@ -121,6 +122,10 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V
if (viewHolder.mExpandButton.getTag() == null) {
viewHolder.mExpandButton.setTag(R.drawable.btn_expand_more);
}
+
+ setAccessibility(view, viewType,
+ AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expand);
+
// Add expand buttons if the permissions are more than PERMISSION_SIZE in this list also
// make the summary invisible by default.
if (mPermissions.size() > PERMISSION_SIZE) {
@@ -132,10 +137,18 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V
viewHolder.mExpandButton.setImageResource(R.drawable.btn_expand_less);
viewHolder.mPermissionSummary.setVisibility(View.VISIBLE);
viewHolder.mExpandButton.setTag(R.drawable.btn_expand_less);
+ view.setContentDescription(mContext.getString(R.string.permission_expanded));
+ setAccessibility(view, viewType,
+ AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_collapse);
+ viewHolder.mPermissionSummary.setFocusable(true);
} else {
viewHolder.mExpandButton.setImageResource(R.drawable.btn_expand_more);
viewHolder.mPermissionSummary.setVisibility(View.GONE);
viewHolder.mExpandButton.setTag(R.drawable.btn_expand_more);
+ view.setContentDescription(mContext.getString(R.string.permission_collapsed));
+ setAccessibility(view, viewType,
+ AccessibilityNodeInfo.ACTION_CLICK, R.string.permission_expanded);
+ viewHolder.mPermissionSummary.setFocusable(false);
}
});
} else {
@@ -187,6 +200,18 @@ class PermissionListAdapter extends RecyclerView.Adapter<PermissionListAdapter.V
}
}
+ private void setAccessibility(View view, int viewType, int action, int resourceId) {
+ final String actionString = mContext.getString(resourceId);
+ final String permission = mContext.getString(sTitleMap.get(viewType));
+ view.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(action,
+ actionString + permission));
+ }
+ });
+ }
+
void setPermissionType(List<Integer> permissions) {
mPermissions = permissions;
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
index b60aba8be6f7..e6710fff1bd5 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
@@ -83,7 +83,7 @@ public class UninstallUninstalling extends Activity implements
}
UserManager customUserManager = UninstallUninstalling.this
- .createContextAsUser(UserHandle.of(user.getIdentifier()), 0)
+ .createContextAsUser(user, 0)
.getSystemService(UserManager.class);
if (customUserManager.isUserOfType(UserManager.USER_TYPE_PROFILE_CLONE)) {
isCloneUser = true;
@@ -117,7 +117,7 @@ public class UninstallUninstalling extends Activity implements
int flags = allUsers ? PackageManager.DELETE_ALL_USERS : 0;
flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0;
- getPackageManager().getPackageInstaller().uninstall(
+ createContextAsUser(user, 0).getPackageManager().getPackageInstaller().uninstall(
new VersionedPackage(mAppInfo.packageName,
PackageManager.VERSION_CODE_HIGHEST),
flags, pendingIntent.getIntentSender());
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 7250bdd4bec6..9c67817cbef4 100755..100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -367,10 +367,10 @@ public class UninstallerActivity extends Activity {
int flags = mDialogInfo.allUsers ? PackageManager.DELETE_ALL_USERS : 0;
flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0;
- getPackageManager().getPackageInstaller().uninstall(
- new VersionedPackage(mDialogInfo.appInfo.packageName,
- PackageManager.VERSION_CODE_HIGHEST),
- flags, pendingIntent.getIntentSender());
+ createContextAsUser(mDialogInfo.user, 0).getPackageManager().getPackageInstaller()
+ .uninstall(new VersionedPackage(mDialogInfo.appInfo.packageName,
+ PackageManager.VERSION_CODE_HIGHEST), flags,
+ pendingIntent.getIntentSender());
} catch (Exception e) {
notificationManager.cancel(uninstallId);
diff --git a/packages/SettingsLib/res/drawable/dialog_btn_filled.xml b/packages/SettingsLib/res/drawable/dialog_btn_filled.xml
new file mode 100644
index 000000000000..14cb1de9fa2d
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/dialog_btn_filled.xml
@@ -0,0 +1,39 @@
+<?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.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:insetTop="@dimen/dialog_button_vertical_inset"
+ android:insetBottom="@dimen/dialog_button_vertical_inset">
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+ <padding android:left="@dimen/dialog_button_horizontal_padding"
+ android:top="@dimen/dialog_button_vertical_padding"
+ android:right="@dimen/dialog_button_horizontal_padding"
+ android:bottom="@dimen/dialog_button_vertical_padding"/>
+ </shape>
+ </item>
+ </ripple>
+</inset>
diff --git a/packages/SettingsLib/res/drawable/dialog_btn_outline.xml b/packages/SettingsLib/res/drawable/dialog_btn_outline.xml
new file mode 100644
index 000000000000..1e7775980243
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/dialog_btn_outline.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:insetTop="@dimen/dialog_button_vertical_inset"
+ android:insetBottom="@dimen/dialog_button_vertical_inset">
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ <solid android:color="@android:color/transparent"/>
+ <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant"
+ android:width="1dp"
+ />
+ <padding android:left="@dimen/dialog_button_horizontal_padding"
+ android:top="@dimen/dialog_button_vertical_padding"
+ android:right="@dimen/dialog_button_horizontal_padding"
+ android:bottom="@dimen/dialog_button_vertical_padding"/>
+ </shape>
+ </item>
+ </ripple>
+</inset>
diff --git a/packages/SettingsLib/res/drawable/ic_admin_panel_settings.xml b/packages/SettingsLib/res/drawable/ic_admin_panel_settings.xml
new file mode 100644
index 000000000000..3ea8e9e67d4e
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_admin_panel_settings.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M17,17Q17.625,17 18.062,16.562Q18.5,16.125 18.5,15.5Q18.5,14.875 18.062,14.438Q17.625,14 17,14Q16.375,14 15.938,14.438Q15.5,14.875 15.5,15.5Q15.5,16.125 15.938,16.562Q16.375,17 17,17ZM17,20Q17.775,20 18.425,19.637Q19.075,19.275 19.475,18.675Q18.925,18.35 18.3,18.175Q17.675,18 17,18Q16.325,18 15.7,18.175Q15.075,18.35 14.525,18.675Q14.925,19.275 15.575,19.637Q16.225,20 17,20ZM12,22Q8.525,21.125 6.263,18.012Q4,14.9 4,11.1V5L12,2L20,5V10.675Q19.525,10.475 19.025,10.312Q18.525,10.15 18,10.075V6.4L12,4.15L6,6.4V11.1Q6,12.275 6.312,13.45Q6.625,14.625 7.188,15.688Q7.75,16.75 8.55,17.65Q9.35,18.55 10.325,19.15Q10.6,19.95 11.05,20.675Q11.5,21.4 12.075,21.975Q12.05,21.975 12.038,21.988Q12.025,22 12,22ZM17,22Q14.925,22 13.463,20.538Q12,19.075 12,17Q12,14.925 13.463,13.462Q14.925,12 17,12Q19.075,12 20.538,13.462Q22,14.925 22,17Q22,19.075 20.538,20.538Q19.075,22 17,22ZM12,11.65Q12,11.65 12,11.65Q12,11.65 12,11.65Q12,11.65 12,11.65Q12,11.65 12,11.65Q12,11.65 12,11.65Q12,11.65 12,11.65Q12,11.65 12,11.65Q12,11.65 12,11.65Q12,11.65 12,11.65Q12,11.65 12,11.65Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/layout/dialog_with_icon.xml b/packages/SettingsLib/res/layout/dialog_with_icon.xml
new file mode 100644
index 000000000000..9081ca5cc1bb
--- /dev/null
+++ b/packages/SettingsLib/res/layout/dialog_with_icon.xml
@@ -0,0 +1,99 @@
+<!--
+ ~ 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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:padding="@dimen/grant_admin_dialog_padding"
+ android:paddingBottom="0dp">
+ <ImageView
+ android:id="@+id/dialog_with_icon_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription=""/>
+ <TextView
+ android:id="@+id/dialog_with_icon_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ style="@style/DialogWithIconTitle"
+ android:text="@string/user_grant_admin_title"
+ android:textDirection="locale"/>
+ <TextView
+ android:id="@+id/dialog_with_icon_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dp"
+ android:gravity="center"
+ style="@style/TextAppearanceSmall"
+ android:text=""
+ android:textDirection="locale"/>
+ <LinearLayout
+ android:id="@+id/custom_layout"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:paddingBottom="0dp">
+ </LinearLayout>
+ <LinearLayout
+ android:id="@+id/button_panel"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:paddingBottom="0dp">
+ <Button
+ android:id="@+id/button_cancel"
+ style="@style/DialogButtonNegative"
+ android:layout_width="wrap_content"
+ android:buttonCornerRadius="28dp"
+ android:layout_height="wrap_content"
+ android:visibility="gone"/>
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" >
+ </Space>
+ <Button
+ android:id="@+id/button_back"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/DialogButtonNegative"
+ android:buttonCornerRadius="40dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:text="Back"
+ android:visibility="gone"
+ />
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="0.05"
+ >
+ </Space>
+ <Button
+ android:id="@+id/button_ok"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/DialogButtonPositive"
+ android:clickable="true"
+ android:focusable="true"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index dbfd1c27fce8..e9aded0838d9 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -112,4 +112,13 @@
<!-- Size of grant admin privileges dialog padding -->
<dimen name="grant_admin_dialog_padding">16dp</dimen>
+
+ <dimen name="dialog_button_horizontal_padding">16dp</dimen>
+ <dimen name="dialog_button_vertical_padding">8dp</dimen>
+ <!-- The button will be 48dp tall, but the background needs to be 36dp tall -->
+ <dimen name="dialog_button_vertical_inset">6dp</dimen>
+ <dimen name="dialog_top_padding">24dp</dimen>
+ <dimen name="dialog_bottom_padding">18dp</dimen>
+ <dimen name="dialog_side_padding">24dp</dimen>
+ <dimen name="dialog_button_bar_top_padding">32dp</dimen>
</resources>
diff --git a/packages/SettingsLib/res/values/styles.xml b/packages/SettingsLib/res/values/styles.xml
index cc60382b48f9..2584be725747 100644
--- a/packages/SettingsLib/res/values/styles.xml
+++ b/packages/SettingsLib/res/values/styles.xml
@@ -83,4 +83,39 @@
<item name="android:textDirection">locale</item>
<item name="android:ellipsize">end</item>
</style>
+
+ <style name="DialogWithIconTitle" parent="@android:TextAppearance.DeviceDefault.Headline">
+ <item name="android:textSize">@dimen/broadcast_dialog_title_text_size</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textDirection">locale</item>
+ <item name="android:ellipsize">end</item>
+ </style>
+
+ <style name="DialogButtonPositive" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item name="android:buttonCornerRadius">0dp</item>
+ <item name="android:background">@drawable/dialog_btn_filled</item>
+ <item name="android:textColor">?androidprv:attr/textColorOnAccent</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:lineHeight">20sp</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
+ <item name="android:stateListAnimator">@null</item>
+ <item name="android:minWidth">0dp</item>
+ </style>
+
+ <style name="DialogButtonNegative">
+ <item name="android:buttonCornerRadius">28dp</item>
+ <item name="android:background">@drawable/dialog_btn_outline</item>
+ <item name="android:buttonCornerRadius">28dp</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:lineHeight">20sp</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
+ <item name="android:stateListAnimator">@null</item>
+ <item name="android:minWidth">0dp</item>
+ </style>
+
+ <style name="TextStyleMessage">
+ <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+ <item name="android:textSize">16dp</item>
+ </style>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java b/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java
new file mode 100644
index 000000000000..de488144be6c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java
@@ -0,0 +1,277 @@
+/*
+ * 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.utils;
+import android.annotation.IntDef;
+import android.annotation.StringRes;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.settingslib.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class is used to create custom dialog with icon, title, message and custom view that are
+ * horizontally centered.
+ */
+public class CustomDialogHelper {
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ICON, TITLE, MESSAGE, LAYOUT, BACK_BUTTON, NEGATIVE_BUTTON, POSITIVE_BUTTON})
+ public @interface LayoutComponent {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({BACK_BUTTON, NEGATIVE_BUTTON, POSITIVE_BUTTON})
+ public @interface LayoutButton {}
+
+ public static final int ICON = 0;
+ public static final int TITLE = 1;
+ public static final int MESSAGE = 2;
+ public static final int LAYOUT = 3;
+ public static final int BACK_BUTTON = 4;
+ public static final int NEGATIVE_BUTTON = 5;
+ public static final int POSITIVE_BUTTON = 6;
+ private View mDialogContent;
+ private Dialog mDialog;
+ private Context mContext;
+ private LayoutInflater mLayoutInflater;
+ private ImageView mDialogIcon;
+ private TextView mDialogTitle;
+ private TextView mDialogMessage;
+ private LinearLayout mCustomLayout;
+ private Button mPositiveButton;
+ private Button mNegativeButton;
+ private Button mBackButton;
+
+ public CustomDialogHelper(Context context) {
+ mContext = context;
+ mLayoutInflater = LayoutInflater.from(context);
+ mDialogContent = mLayoutInflater.inflate(R.layout.dialog_with_icon, null);
+ mDialogIcon = mDialogContent.findViewById(R.id.dialog_with_icon_icon);
+ mDialogTitle = mDialogContent.findViewById(R.id.dialog_with_icon_title);
+ mDialogMessage = mDialogContent.findViewById(R.id.dialog_with_icon_message);
+ mCustomLayout = mDialogContent.findViewById(R.id.custom_layout);
+ mPositiveButton = mDialogContent.findViewById(R.id.button_ok);
+ mNegativeButton = mDialogContent.findViewById(R.id.button_cancel);
+ mBackButton = mDialogContent.findViewById(R.id.button_back);
+ createDialog();
+ }
+
+ /**
+ * Creates dialog with content defined in constructor.
+ */
+ private void createDialog() {
+ mDialog = new AlertDialog.Builder(mContext)
+ .setView(mDialogContent)
+ .setCancelable(true)
+ .create();
+ mDialog.getWindow()
+ .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ }
+
+ /**
+ * Sets title and listener for positive button.
+ */
+ public CustomDialogHelper setPositiveButton(@StringRes int resid,
+ View.OnClickListener onClickListener) {
+ setButton(POSITIVE_BUTTON, resid, onClickListener);
+ return this;
+ }
+
+ /**
+ * Sets positive button text.
+ */
+ public CustomDialogHelper setPositiveButtonText(@StringRes int resid) {
+ mPositiveButton.setText(resid);
+ return this;
+ }
+
+ /**
+ * Sets title and listener for negative button.
+ */
+ public CustomDialogHelper setNegativeButton(@StringRes int resid,
+ View.OnClickListener onClickListener) {
+ setButton(NEGATIVE_BUTTON, resid, onClickListener);
+ return this;
+ }
+
+ /**
+ * Sets negative button text.
+ */
+ public CustomDialogHelper setNegativeButtonText(@StringRes int resid) {
+ mNegativeButton.setText(resid);
+ return this;
+ }
+
+ /**
+ * Sets title and listener for back button.
+ */
+ public CustomDialogHelper setBackButton(@StringRes int resid,
+ View.OnClickListener onClickListener) {
+ setButton(BACK_BUTTON, resid, onClickListener);
+ return this;
+ }
+
+ /**
+ * Sets title for back button.
+ */
+ public CustomDialogHelper setBackButtonText(@StringRes int resid) {
+ mBackButton.setText(resid);
+ return this;
+ }
+
+ private void setButton(@LayoutButton int whichButton, @StringRes int resid,
+ View.OnClickListener listener) {
+ switch (whichButton) {
+ case POSITIVE_BUTTON :
+ mPositiveButton.setText(resid);
+ mPositiveButton.setVisibility(View.VISIBLE);
+ mPositiveButton.setOnClickListener(listener);
+ break;
+ case NEGATIVE_BUTTON:
+ mNegativeButton.setText(resid);
+ mNegativeButton.setVisibility(View.VISIBLE);
+ mNegativeButton.setOnClickListener(listener);
+ break;
+ case BACK_BUTTON:
+ mBackButton.setText(resid);
+ mBackButton.setVisibility(View.VISIBLE);
+ mBackButton.setOnClickListener(listener);
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ /**
+ * Modifies state of button.
+ * //TODO: modify method to allow setting state for any button.
+ */
+ public CustomDialogHelper setButtonEnabled(boolean enabled) {
+ mPositiveButton.setEnabled(enabled);
+ return this;
+ }
+
+ /**
+ * Sets title of the dialog.
+ */
+ public CustomDialogHelper setTitle(@StringRes int resid) {
+ mDialogTitle.setText(resid);
+ return this;
+ }
+
+ /**
+ * Sets message of the dialog.
+ */
+ public CustomDialogHelper setMessage(@StringRes int resid) {
+ mDialogMessage.setText(resid);
+ return this;
+ }
+
+ /**
+ * Sets icon of the dialog.
+ */
+ public CustomDialogHelper setIcon(Drawable icon) {
+ mDialogIcon.setImageDrawable(icon);
+ return this;
+ }
+
+ /**
+ * Removes all views that were previously added to the custom layout part.
+ */
+ public CustomDialogHelper clearCustomLayout() {
+ mCustomLayout.removeAllViews();
+ return this;
+ }
+
+ /**
+ * Hides custom layout.
+ */
+ public void hideCustomLayout() {
+ mCustomLayout.setVisibility(View.GONE);
+ }
+
+ /**
+ * Shows custom layout.
+ */
+ public void showCustomLayout() {
+ mCustomLayout.setVisibility(View.VISIBLE);
+ }
+
+ /**
+ * Adds view to custom layout.
+ */
+ public CustomDialogHelper addCustomView(View view) {
+ mCustomLayout.addView(view);
+ return this;
+ }
+
+ /**
+ * Returns dialog.
+ */
+ public Dialog getDialog() {
+ return mDialog;
+ }
+
+ /**
+ * Sets visibility of layout component.
+ * @param element part of the layout visibility of which is being changed.
+ * @param isVisible true if visibility is set to View.VISIBLE
+ * @return this
+ */
+ public CustomDialogHelper setVisibility(@LayoutComponent int element, boolean isVisible) {
+ int visibility;
+ if (isVisible) {
+ visibility = View.VISIBLE;
+ } else {
+ visibility = View.GONE;
+ }
+ switch (element) {
+ case ICON:
+ mDialogIcon.setVisibility(visibility);
+ break;
+ case TITLE:
+ mDialogTitle.setVisibility(visibility);
+ break;
+ case MESSAGE:
+ mDialogMessage.setVisibility(visibility);
+ break;
+ case BACK_BUTTON:
+ mBackButton.setVisibility(visibility);
+ break;
+ case NEGATIVE_BUTTON:
+ mNegativeButton.setVisibility(visibility);
+ break;
+ case POSITIVE_BUTTON:
+ mPositiveButton.setVisibility(visibility);
+ break;
+ default:
+ break;
+ }
+ return this;
+ }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 6a5535d345db..e4cc9f15aea1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -100,5 +100,6 @@ public class SystemSettings {
Settings.System.CAMERA_FLASH_NOTIFICATION,
Settings.System.SCREEN_FLASH_NOTIFICATION,
Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
+ Settings.System.SMOOTH_DISPLAY
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 85623b26c589..4b720636c1d4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -226,5 +226,6 @@ public class SystemSettingsValidators {
VALIDATORS.put(System.CAMERA_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION_COLOR, ANY_INTEGER_VALIDATOR);
+ VALIDATORS.put(System.SMOOTH_DISPLAY, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 6d375ac215a4..48259e165670 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -46,12 +46,16 @@ import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManage
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
+import java.util.Set;
public class SettingsHelper {
private static final String TAG = "SettingsHelper";
private static final String SILENT_RINGTONE = "_silent";
private static final String SETTINGS_REPLACED_KEY = "backup_skip_user_facing_data";
private static final String SETTING_ORIGINAL_KEY_SUFFIX = "_original";
+ private static final String UNICODE_LOCALE_EXTENSION_FW = "fw";
+ private static final String UNICODE_LOCALE_EXTENSION_MU = "mu";
+ private static final String UNICODE_LOCALE_EXTENSION_NU = "nu";
private static final float FLOAT_TOLERANCE = 0.01f;
/** See frameworks/base/core/res/res/values/config.xml#config_longPressOnPowerBehavior **/
@@ -97,6 +101,25 @@ public class SettingsHelper {
sBroadcastOnRestoreSystemUI.add(Settings.Secure.QS_AUTO_ADDED_TILES);
}
+ private static final ArraySet<String> UNICODE_LOCALE_SUPPORTED_EXTENSIONS = new ArraySet<>();
+
+ /**
+ * Current supported extensions are fw (first day of week) and mu (temperature unit) extension.
+ * User can set these extensions in Settings app, and it will be appended to the locale,
+ * for example: zh-Hant-TW-u-fw-mon-mu-celsius. So after the factory reset, these extensions
+ * should be restored as well because they are set by users.
+ * We do not put the nu (numbering system) extension here because it is an Android supported
+ * extension and defined in some particular locales, for example:
+ * ar-Arab-MA-u-nu-arab and ar-Arab-YE-u-nu-latn. See
+ * <code>frameworks/base/core/res/res/values/locale_config.xml</code>
+ * The nu extension should not be appended to the current/restored locale after factory reset
+ * if the current/restored locale does not have it.
+ */
+ static {
+ UNICODE_LOCALE_SUPPORTED_EXTENSIONS.add(UNICODE_LOCALE_EXTENSION_FW);
+ UNICODE_LOCALE_SUPPORTED_EXTENSIONS.add(UNICODE_LOCALE_EXTENSION_MU);
+ }
+
private interface SettingsLookup {
public String lookup(ContentResolver resolver, String name, int userHandle);
}
@@ -500,20 +523,25 @@ public class SettingsHelper {
allLocales.put(toFullLocale(locale), locale);
}
+ // After restoring to reset locales, need to get extensions from restored locale. Get the
+ // first restored locale to check its extension.
+ final Locale restoredLocale = restore.isEmpty()
+ ? Locale.ROOT
+ : restore.get(0);
final ArrayList<Locale> filtered = new ArrayList<>(current.size());
for (int i = 0; i < current.size(); i++) {
- final Locale locale = current.get(i);
+ Locale locale = copyExtensionToTargetLocale(restoredLocale, current.get(i));
allLocales.remove(toFullLocale(locale));
filtered.add(locale);
}
for (int i = 0; i < restore.size(); i++) {
- final Locale locale = allLocales.remove(toFullLocale(restore.get(i)));
- if (locale != null) {
- filtered.add(locale);
+ final Locale restoredLocaleWithExtension = copyExtensionToTargetLocale(restoredLocale,
+ getFilteredLocale(restore.get(i), allLocales));
+ if (restoredLocaleWithExtension != null) {
+ filtered.add(restoredLocaleWithExtension);
}
}
-
if (filtered.size() == current.size()) {
return current; // Nothing added to current locale list.
}
@@ -521,6 +549,45 @@ public class SettingsHelper {
return new LocaleList(filtered.toArray(new Locale[filtered.size()]));
}
+ private static Locale copyExtensionToTargetLocale(Locale restoredLocale,
+ Locale targetLocale) {
+ if (!restoredLocale.hasExtensions()) {
+ return targetLocale;
+ }
+
+ if (targetLocale == null) {
+ return null;
+ }
+
+ Locale.Builder builder = new Locale.Builder()
+ .setLocale(targetLocale);
+ Set<String> unicodeLocaleKeys = restoredLocale.getUnicodeLocaleKeys();
+ unicodeLocaleKeys.stream().forEach(key -> {
+ // Copy all supported extensions from restored locales except "nu" extension. The "nu"
+ // extension has been added in #getFilteredLocale(Locale, HashMap<Locale, Locale>)
+ // already, we don't need to add it again.
+ if (UNICODE_LOCALE_SUPPORTED_EXTENSIONS.contains(key)) {
+ builder.setUnicodeLocaleKeyword(key, restoredLocale.getUnicodeLocaleType(key));
+ }
+ });
+ return builder.build();
+ }
+
+ private static Locale getFilteredLocale(Locale restoreLocale,
+ HashMap<Locale, Locale> allLocales) {
+ Locale locale = allLocales.remove(toFullLocale(restoreLocale));
+ if (locale != null) {
+ return locale;
+ }
+
+ Locale filteredLocale = new Locale.Builder()
+ .setLocale(restoreLocale.stripExtensions())
+ .setUnicodeLocaleKeyword(UNICODE_LOCALE_EXTENSION_NU,
+ restoreLocale.getUnicodeLocaleType(UNICODE_LOCALE_EXTENSION_NU))
+ .build();
+ return allLocales.remove(toFullLocale(filteredLocale));
+ }
+
/**
* Sets the locale specified. Input data is the byte representation of comma separated
* multiple BCP-47 language tags. For backwards compatibility, strings of the form
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 284b06b86cb6..d1bd5e661072 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -34,6 +34,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OV
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.internal.accessibility.util.AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM;
+import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX;
import static com.android.providers.settings.SettingsState.getTypeFromKey;
import static com.android.providers.settings.SettingsState.getUserIdFromKey;
@@ -3748,7 +3749,7 @@ public class SettingsProvider extends ContentProvider {
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 218;
+ private static final int SETTINGS_VERSION = 219;
private final int mUserId;
@@ -5673,7 +5674,7 @@ public class SettingsProvider extends ContentProvider {
providers.addAll(Arrays.asList(resources.getStringArray(resourceId)));
} catch (Resources.NotFoundException e) {
Slog.w(LOG_TAG,
- "Get default array Cred Provider not found: " + e.toString());
+ "Get default array Cred Provider not found: " + e.toString());
}
try {
final String storedValue = resources.getString(resourceId);
@@ -5682,7 +5683,7 @@ public class SettingsProvider extends ContentProvider {
}
} catch (Resources.NotFoundException e) {
Slog.w(LOG_TAG,
- "Get default Cred Provider not found: " + e.toString());
+ "Get default Cred Provider not found: " + e.toString());
}
if (!providers.isEmpty()) {
@@ -5731,8 +5732,8 @@ public class SettingsProvider extends ContentProvider {
final Setting currentSetting = secureSettings
.getSettingLocked(Settings.Secure.CREDENTIAL_SERVICE);
if (currentSetting.isNull()) {
- final int resourceId =
- com.android.internal.R.array.config_defaultCredentialProviderService;
+ final int resourceId = com.android.internal.R.array
+ .config_defaultCredentialProviderService;
final Resources resources = getContext().getResources();
// If the config has not be defined we might get an exception.
final List<String> providers = new ArrayList<>();
@@ -5740,7 +5741,7 @@ public class SettingsProvider extends ContentProvider {
providers.addAll(Arrays.asList(resources.getStringArray(resourceId)));
} catch (Resources.NotFoundException e) {
Slog.w(LOG_TAG,
- "Get default array Cred Provider not found: " + e.toString());
+ "Get default array Cred Provider not found: " + e.toString());
}
if (!providers.isEmpty()) {
@@ -5839,6 +5840,47 @@ public class SettingsProvider extends ContentProvider {
currentVersion = 218;
}
+ // v218: Convert Smooth Display and Force Peak Refresh Rate to a boolean
+ if (currentVersion == 218) {
+ final String peakRefreshRateSettingName = "peak_refresh_rate";
+ final String minRefreshRateSettingName = "min_refresh_rate";
+
+ final SettingsState systemSettings = getSystemSettingsLocked(userId);
+ final Setting peakRefreshRateSetting =
+ systemSettings.getSettingLocked(peakRefreshRateSettingName);
+ final Setting minRefreshRateSetting =
+ systemSettings.getSettingLocked(minRefreshRateSettingName);
+
+ float peakRefreshRate = DEFAULT_REFRESH_RATE;
+ float minRefreshRate = 0;
+ try {
+ if (!peakRefreshRateSetting.isNull()) {
+ peakRefreshRate = Float.parseFloat(peakRefreshRateSetting.getValue());
+ }
+ } catch (NumberFormatException e) {
+ // Do nothing. Overwrite with default value.
+ }
+ try {
+ if (!minRefreshRateSetting.isNull()) {
+ minRefreshRate = Float.parseFloat(minRefreshRateSetting.getValue());
+ }
+ } catch (NumberFormatException e) {
+ // Do nothing. Overwrite with default value.
+ }
+
+ systemSettings.deleteSettingLocked(peakRefreshRateSettingName);
+ systemSettings.deleteSettingLocked(minRefreshRateSettingName);
+
+ systemSettings.insertSettingLocked(Settings.System.SMOOTH_DISPLAY,
+ peakRefreshRate > DEFAULT_REFRESH_RATE ? "1" : "0", /* tag= */ null,
+ /* makeDefault= */ false, SettingsState.SYSTEM_PACKAGE_NAME);
+ systemSettings.insertSettingLocked(Settings.System.FORCE_PEAK_REFRESH_RATE,
+ minRefreshRate > 0 ? "1" : "0", /* tag= */ null,
+ /* makeDefault= */ false, SettingsState.SYSTEM_PACKAGE_NAME);
+
+ currentVersion = 219;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 706666cbebab..36aa2ac74406 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -97,8 +97,7 @@ public class SettingsBackupTest {
Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
Settings.System.DESKTOP_MODE, // developer setting for internal prototyping
- Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities
- Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
+ Settings.System.FORCE_PEAK_REFRESH_RATE, // depends on hardware capabilities
Settings.System.SCREEN_BRIGHTNESS_FLOAT,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
index ee76dbf8ce70..bc81c4441af5 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
@@ -299,12 +299,42 @@ public class SettingsHelperTest {
LocaleList.forLanguageTags("en-US"), // current
new String[] { "en-US", "zh-Hans-CN" })); // supported
- // Old langauge code should be updated.
+ // Old language code should be updated.
assertEquals(LocaleList.forLanguageTags("en-US,he-IL,id-ID,yi"),
SettingsHelper.resolveLocales(
LocaleList.forLanguageTags("iw-IL,in-ID,ji"), // restore
LocaleList.forLanguageTags("en-US"), // current
new String[] { "he-IL", "id-ID", "yi" })); // supported
+
+ // No matter the current locale has "nu" extension or not, if the restored locale has fw
+ // (first day of week) or mu(temperature unit) extension, we should restore fw or mu
+ // extensions as well and append these to restore and current locales.
+ assertEquals(LocaleList.forLanguageTags(
+ "en-US-u-fw-mon-mu-celsius,zh-Hant-TW-u-fw-mon-mu-celsius"),
+ SettingsHelper.resolveLocales(
+ LocaleList.forLanguageTags("zh-Hant-TW-u-fw-mon-mu-celsius"), // restore
+ LocaleList.forLanguageTags("en-US"), // current
+ new String[] { "en-US", "zh-Hant-TW" })); // supported
+
+ // No matter the current locale has "nu" extension or not, if the restored locale has fw
+ // (first day of week) or mu(temperature unit) extension, we should restore fw or mu
+ // extensions as well and append these to restore and current locales.
+ assertEquals(LocaleList.forLanguageTags(
+ "fa-Arab-AF-u-nu-latn-fw-mon-mu-celsius,zh-Hant-TW-u-fw-mon-mu-celsius"),
+ SettingsHelper.resolveLocales(
+ LocaleList.forLanguageTags("zh-Hant-TW-u-fw-mon-mu-celsius"), // restore
+ LocaleList.forLanguageTags("fa-Arab-AF-u-nu-latn"), // current
+ new String[] { "fa-Arab-AF-u-nu-latn", "zh-Hant-TW" })); // supported
+
+ // If the restored locale only has nu extension, we should not restore the nu extensions to
+ // current locales.
+ assertEquals(LocaleList.forLanguageTags("zh-Hant-TW,fa-Arab-AF-u-nu-latn"),
+ SettingsHelper.resolveLocales(
+ LocaleList.forLanguageTags("fa-Arab-AF-u-nu-latn"), // restore
+ LocaleList.forLanguageTags("zh-Hant-TW"), // current
+ new String[] { "fa-Arab-AF-u-nu-latn", "zh-Hant-TW" })); // supported
+
+
}
@Test
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 751fbaad2abe..a110f56d09bd 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -23,7 +23,9 @@
>
<!-- Standard permissions granted to the shell. -->
+ <uses-permission android:name="android.permission.MANAGE_HEALTH_PERMISSIONS" />
<uses-permission android:name="android.permission.MANAGE_HEALTH_DATA" />
+ <uses-permission android:name="android.permission.health.READ_EXERCISE_ROUTE" />
<uses-permission android:name="android.permission.MIGRATE_HEALTH_CONNECT_DATA" />
<uses-permission android:name="android.permission.LAUNCH_DEVICE_MANAGER_SETUP" />
<uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 9d32e905d85d..865b0dfe0ee8 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -173,6 +173,7 @@ android_library {
"androidx.palette_palette",
"androidx.legacy_legacy-preference-v14",
"androidx.leanback_leanback",
+ "androidx.tracing_tracing",
"androidx.slice_slice-core",
"androidx.slice_slice-view",
"androidx.slice_slice-builders",
@@ -243,15 +244,11 @@ filegroup {
// domain
"tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt",
"tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt",
"tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt",
"tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt",
"tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt",
"tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt",
"tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt",
- "tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt",
// ui
"tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt",
"tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a00f401756f7..4652ef195a0c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -380,6 +380,9 @@
<service android:name="SystemUIService"
android:exported="true"
/>
+ <service android:name=".wallet.controller.WalletContextualLocationsService"
+ android:exported="true"
+ />
<!-- Service for dumping extremely verbose content during a bug report -->
<service android:name=".dump.SystemUIAuxiliaryDumpService"
@@ -979,6 +982,8 @@
</intent-filter>
</activity>
+ <service android:name=".notetask.NoteTaskControllerUpdateService" />
+
<activity
android:name=".notetask.shortcut.LaunchNoteTaskActivity"
android:exported="true"
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index 5b5871f95fb3..8eb012d2e41c 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -43,6 +43,7 @@ android_library {
"androidx.core_core-ktx",
"androidx.annotation_annotation",
"SystemUIShaderLib",
+ "animationlib",
],
manifest: "AndroidManifest.xml",
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 296c2ae5cf99..2e803798fccb 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -39,9 +39,9 @@ import android.view.animation.Interpolator
import android.view.animation.PathInterpolator
import androidx.annotation.BinderThread
import androidx.annotation.UiThread
+import com.android.app.animation.Interpolators
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
-import java.lang.IllegalArgumentException
import kotlin.math.roundToInt
private const val TAG = "ActivityLaunchAnimator"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 42a86363bf01..48dd08f206c1 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -33,10 +33,10 @@ import android.view.WindowInsets
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
import android.widget.FrameLayout
+import com.android.app.animation.Interpolators
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CujType
import com.android.systemui.util.registerAnimationOnBackInvoked
-import java.lang.IllegalArgumentException
import kotlin.math.roundToInt
private const val TAG = "DialogLaunchAnimator"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
index f0a82113c3a3..83e44b69812b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
@@ -18,8 +18,10 @@ package com.android.systemui.animation
import android.graphics.fonts.Font
import android.graphics.fonts.FontVariationAxis
+import android.util.LruCache
import android.util.MathUtils
import android.util.MathUtils.abs
+import androidx.annotation.VisibleForTesting
import java.lang.Float.max
import java.lang.Float.min
@@ -34,6 +36,10 @@ private const val FONT_ITALIC_MIN = 0f
private const val FONT_ITALIC_ANIMATION_STEP = 0.1f
private const val FONT_ITALIC_DEFAULT_VALUE = 0f
+// Benchmarked via Perfetto, difference between 10 and 50 entries is about 0.3ms in
+// frame draw time on a Pixel 6.
+@VisibleForTesting const val FONT_CACHE_MAX_ENTRIES = 10
+
/** Provide interpolation of two fonts by adjusting font variation settings. */
class FontInterpolator {
@@ -81,8 +87,8 @@ class FontInterpolator {
// Font interpolator has two level caches: one for input and one for font with different
// variation settings. No synchronization is needed since FontInterpolator is not designed to be
// thread-safe and can be used only on UI thread.
- private val interpCache = hashMapOf<InterpKey, Font>()
- private val verFontCache = hashMapOf<VarFontKey, Font>()
+ private val interpCache = LruCache<InterpKey, Font>(FONT_CACHE_MAX_ENTRIES)
+ private val verFontCache = LruCache<VarFontKey, Font>(FONT_CACHE_MAX_ENTRIES)
// Mutable keys for recycling.
private val tmpInterpKey = InterpKey(null, null, 0f)
@@ -152,7 +158,7 @@ class FontInterpolator {
tmpVarFontKey.set(start, newAxes)
val axesCachedFont = verFontCache[tmpVarFontKey]
if (axesCachedFont != null) {
- interpCache[InterpKey(start, end, progress)] = axesCachedFont
+ interpCache.put(InterpKey(start, end, progress), axesCachedFont)
return axesCachedFont
}
@@ -160,8 +166,8 @@ class FontInterpolator {
// Font.Builder#build won't throw IOException since creating fonts from existing fonts will
// not do any IO work.
val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build()
- interpCache[InterpKey(start, end, progress)] = newFont
- verFontCache[VarFontKey(start, newAxes)] = newFont
+ interpCache.put(InterpKey(start, end, progress), newFont)
+ verFontCache.put(VarFontKey(start, newAxes), newFont)
return newFont
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
deleted file mode 100644
index 9dbb9205b90e..000000000000
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2016 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.animation;
-
-import android.graphics.Path;
-import android.util.MathUtils;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.BounceInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.PathInterpolator;
-
-/**
- * Utility class to receive interpolators from.
- *
- * Make sure that changes made to this class are also reflected in {@link InterpolatorsAndroidX}.
- * Please consider using the androidx dependencies featuring better testability altogether.
- */
-public class Interpolators {
-
- /*
- * ============================================================================================
- * Emphasized interpolators.
- * ============================================================================================
- */
-
- /**
- * The default emphasized interpolator. Used for hero / emphasized movement of content.
- */
- public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
-
- /**
- * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
- * is disappearing e.g. when moving off screen.
- */
- public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
- 0.3f, 0f, 0.8f, 0.15f);
-
- /**
- * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
- * is appearing e.g. when coming from off screen
- */
- public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
- 0.05f, 0.7f, 0.1f, 1f);
-
-
- /*
- * ============================================================================================
- * Standard interpolators.
- * ============================================================================================
- */
-
- /**
- * The standard interpolator that should be used on every normal animation
- */
- public static final Interpolator STANDARD = new PathInterpolator(
- 0.2f, 0f, 0f, 1f);
-
- /**
- * The standard accelerating interpolator that should be used on every regular movement of
- * content that is disappearing e.g. when moving off screen.
- */
- public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator(
- 0.3f, 0f, 1f, 1f);
-
- /**
- * The standard decelerating interpolator that should be used on every regular movement of
- * content that is appearing e.g. when coming from off screen.
- */
- public static final Interpolator STANDARD_DECELERATE = new PathInterpolator(
- 0f, 0f, 0f, 1f);
-
- /*
- * ============================================================================================
- * Legacy
- * ============================================================================================
- */
-
- /**
- * The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN.
- */
- public static final Interpolator LEGACY = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
-
- /**
- * The default legacy accelerating interpolator as defined in Material 1.
- * Also known as FAST_OUT_LINEAR_IN.
- */
- public static final Interpolator LEGACY_ACCELERATE = new PathInterpolator(0.4f, 0f, 1f, 1f);
-
- /**
- * The default legacy decelerating interpolator as defined in Material 1.
- * Also known as LINEAR_OUT_SLOW_IN.
- */
- public static final Interpolator LEGACY_DECELERATE = new PathInterpolator(0f, 0f, 0.2f, 1f);
-
- /**
- * Linear interpolator. Often used if the interpolator is for different properties who need
- * different interpolations.
- */
- public static final Interpolator LINEAR = new LinearInterpolator();
-
- /*
- * ============================================================================================
- * Custom interpolators
- * ============================================================================================
- */
-
- public static final Interpolator FAST_OUT_SLOW_IN = LEGACY;
- public static final Interpolator FAST_OUT_LINEAR_IN = LEGACY_ACCELERATE;
- public static final Interpolator LINEAR_OUT_SLOW_IN = LEGACY_DECELERATE;
-
- /**
- * Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t
- * goes from 1 to 0 instead of 0 to 1).
- */
- public static final Interpolator FAST_OUT_SLOW_IN_REVERSE =
- new PathInterpolator(0.8f, 0f, 0.6f, 1f);
- public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f);
- public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
- public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
- public static final Interpolator ACCELERATE = new AccelerateInterpolator();
- public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
- public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
- public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f);
- public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
- public static final Interpolator ICON_OVERSHOT_LESS = new PathInterpolator(0.4f, 0f, 0.2f,
- 1.1f);
- public static final Interpolator PANEL_CLOSE_ACCELERATED = new PathInterpolator(0.3f, 0, 0.5f,
- 1);
- public static final Interpolator BOUNCE = new BounceInterpolator();
- /**
- * For state transitions on the control panel that lives in GlobalActions.
- */
- public static final Interpolator CONTROL_STATE = new PathInterpolator(0.4f, 0f, 0.2f,
- 1.0f);
-
- /**
- * Interpolator to be used when animating a move based on a click. Pair with enough duration.
- */
- public static final Interpolator TOUCH_RESPONSE =
- new PathInterpolator(0.3f, 0f, 0.1f, 1f);
-
- /**
- * Like {@link #TOUCH_RESPONSE}, but used in case the animation is played in reverse (i.e. t
- * goes from 1 to 0 instead of 0 to 1).
- */
- public static final Interpolator TOUCH_RESPONSE_REVERSE =
- new PathInterpolator(0.9f, 0f, 0.7f, 1f);
-
- /*
- * ============================================================================================
- * Functions / Utilities
- * ============================================================================================
- */
-
- /**
- * Calculate the amount of overshoot using an exponential falloff function with desired
- * properties, where the overshoot smoothly transitions at the 1.0f boundary into the
- * overshoot, retaining its acceleration.
- *
- * @param progress a progress value going from 0 to 1
- * @param overshootAmount the amount > 0 of overshoot desired. A value of 0.1 means the max
- * value of the overall progress will be at 1.1.
- * @param overshootStart the point in (0,1] where the result should reach 1
- * @return the interpolated overshoot
- */
- public static float getOvershootInterpolation(float progress, float overshootAmount,
- float overshootStart) {
- if (overshootAmount == 0.0f || overshootStart == 0.0f) {
- throw new IllegalArgumentException("Invalid values for overshoot");
- }
- float b = MathUtils.log((overshootAmount + 1) / (overshootAmount)) / overshootStart;
- return MathUtils.max(0.0f,
- (float) (1.0f - Math.exp(-b * progress)) * (overshootAmount + 1.0f));
- }
-
- /**
- * Similar to {@link #getOvershootInterpolation(float, float, float)} but the overshoot
- * starts immediately here, instead of first having a section of non-overshooting
- *
- * @param progress a progress value going from 0 to 1
- */
- public static float getOvershootInterpolation(float progress) {
- return MathUtils.max(0.0f, (float) (1.0f - Math.exp(-4 * progress)));
- }
-
- // Create the default emphasized interpolator
- private static PathInterpolator createEmphasizedInterpolator() {
- Path path = new Path();
- // Doing the same as fast_out_extra_slow_in
- path.moveTo(0f, 0f);
- path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
- path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
- return new PathInterpolator(path);
- }
-}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/InterpolatorsAndroidX.java b/packages/SystemUI/animation/src/com/android/systemui/animation/InterpolatorsAndroidX.java
deleted file mode 100644
index 8da87feb1fee..000000000000
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/InterpolatorsAndroidX.java
+++ /dev/null
@@ -1,219 +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.animation;
-
-import android.graphics.Path;
-import android.util.MathUtils;
-
-import androidx.core.animation.AccelerateDecelerateInterpolator;
-import androidx.core.animation.AccelerateInterpolator;
-import androidx.core.animation.BounceInterpolator;
-import androidx.core.animation.DecelerateInterpolator;
-import androidx.core.animation.Interpolator;
-import androidx.core.animation.LinearInterpolator;
-import androidx.core.animation.PathInterpolator;
-
-/**
- * Utility class to receive interpolators from. (androidx compatible version)
- *
- * This is the androidx compatible version of {@link Interpolators}. Make sure that changes made to
- * this class are also reflected in {@link Interpolators}.
- *
- * Using the androidx versions of {@link androidx.core.animation.ValueAnimator} or
- * {@link androidx.core.animation.ObjectAnimator} improves animation testability. This file provides
- * the androidx compatible versions of the interpolators defined in {@link Interpolators}.
- * AnimatorTestRule can be used in Tests to manipulate the animation under test (e.g. artificially
- * advancing the time).
- */
-public class InterpolatorsAndroidX {
-
- /*
- * ============================================================================================
- * Emphasized interpolators.
- * ============================================================================================
- */
-
- /**
- * The default emphasized interpolator. Used for hero / emphasized movement of content.
- */
- public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
-
- /**
- * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
- * is disappearing e.g. when moving off screen.
- */
- public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
- 0.3f, 0f, 0.8f, 0.15f);
-
- /**
- * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
- * is appearing e.g. when coming from off screen
- */
- public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
- 0.05f, 0.7f, 0.1f, 1f);
-
-
- /*
- * ============================================================================================
- * Standard interpolators.
- * ============================================================================================
- */
-
- /**
- * The standard interpolator that should be used on every normal animation
- */
- public static final Interpolator STANDARD = new PathInterpolator(
- 0.2f, 0f, 0f, 1f);
-
- /**
- * The standard accelerating interpolator that should be used on every regular movement of
- * content that is disappearing e.g. when moving off screen.
- */
- public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator(
- 0.3f, 0f, 1f, 1f);
-
- /**
- * The standard decelerating interpolator that should be used on every regular movement of
- * content that is appearing e.g. when coming from off screen.
- */
- public static final Interpolator STANDARD_DECELERATE = new PathInterpolator(
- 0f, 0f, 0f, 1f);
-
- /*
- * ============================================================================================
- * Legacy
- * ============================================================================================
- */
-
- /**
- * The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN.
- */
- public static final Interpolator LEGACY = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
-
- /**
- * The default legacy accelerating interpolator as defined in Material 1.
- * Also known as FAST_OUT_LINEAR_IN.
- */
- public static final Interpolator LEGACY_ACCELERATE = new PathInterpolator(0.4f, 0f, 1f, 1f);
-
- /**
- * The default legacy decelerating interpolator as defined in Material 1.
- * Also known as LINEAR_OUT_SLOW_IN.
- */
- public static final Interpolator LEGACY_DECELERATE = new PathInterpolator(0f, 0f, 0.2f, 1f);
-
- /**
- * Linear interpolator. Often used if the interpolator is for different properties who need
- * different interpolations.
- */
- public static final Interpolator LINEAR = new LinearInterpolator();
-
- /*
- * ============================================================================================
- * Custom interpolators
- * ============================================================================================
- */
-
- public static final Interpolator FAST_OUT_SLOW_IN = LEGACY;
- public static final Interpolator FAST_OUT_LINEAR_IN = LEGACY_ACCELERATE;
- public static final Interpolator LINEAR_OUT_SLOW_IN = LEGACY_DECELERATE;
-
- /**
- * Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t
- * goes from 1 to 0 instead of 0 to 1).
- */
- public static final Interpolator FAST_OUT_SLOW_IN_REVERSE =
- new PathInterpolator(0.8f, 0f, 0.6f, 1f);
- public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f);
- public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
- public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
- public static final Interpolator ACCELERATE = new AccelerateInterpolator();
- public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
- public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
- public static final Interpolator CUSTOM_40_40 = new PathInterpolator(0.4f, 0f, 0.6f, 1f);
- public static final Interpolator ICON_OVERSHOT = new PathInterpolator(0.4f, 0f, 0.2f, 1.4f);
- public static final Interpolator ICON_OVERSHOT_LESS = new PathInterpolator(0.4f, 0f, 0.2f,
- 1.1f);
- public static final Interpolator PANEL_CLOSE_ACCELERATED = new PathInterpolator(0.3f, 0, 0.5f,
- 1);
- public static final Interpolator BOUNCE = new BounceInterpolator();
- /**
- * For state transitions on the control panel that lives in GlobalActions.
- */
- public static final Interpolator CONTROL_STATE = new PathInterpolator(0.4f, 0f, 0.2f,
- 1.0f);
-
- /**
- * Interpolator to be used when animating a move based on a click. Pair with enough duration.
- */
- public static final Interpolator TOUCH_RESPONSE =
- new PathInterpolator(0.3f, 0f, 0.1f, 1f);
-
- /**
- * Like {@link #TOUCH_RESPONSE}, but used in case the animation is played in reverse (i.e. t
- * goes from 1 to 0 instead of 0 to 1).
- */
- public static final Interpolator TOUCH_RESPONSE_REVERSE =
- new PathInterpolator(0.9f, 0f, 0.7f, 1f);
-
- /*
- * ============================================================================================
- * Functions / Utilities
- * ============================================================================================
- */
-
- /**
- * Calculate the amount of overshoot using an exponential falloff function with desired
- * properties, where the overshoot smoothly transitions at the 1.0f boundary into the
- * overshoot, retaining its acceleration.
- *
- * @param progress a progress value going from 0 to 1
- * @param overshootAmount the amount > 0 of overshoot desired. A value of 0.1 means the max
- * value of the overall progress will be at 1.1.
- * @param overshootStart the point in (0,1] where the result should reach 1
- * @return the interpolated overshoot
- */
- public static float getOvershootInterpolation(float progress, float overshootAmount,
- float overshootStart) {
- if (overshootAmount == 0.0f || overshootStart == 0.0f) {
- throw new IllegalArgumentException("Invalid values for overshoot");
- }
- float b = MathUtils.log((overshootAmount + 1) / (overshootAmount)) / overshootStart;
- return MathUtils.max(0.0f,
- (float) (1.0f - Math.exp(-b * progress)) * (overshootAmount + 1.0f));
- }
-
- /**
- * Similar to {@link #getOvershootInterpolation(float, float, float)} but the overshoot
- * starts immediately here, instead of first having a section of non-overshooting
- *
- * @param progress a progress value going from 0 to 1
- */
- public static float getOvershootInterpolation(float progress) {
- return MathUtils.max(0.0f, (float) (1.0f - Math.exp(-4 * progress)));
- }
-
- // Create the default emphasized interpolator
- private static PathInterpolator createEmphasizedInterpolator() {
- Path path = new Path();
- // Doing the same as fast_out_extra_slow_in
- path.moveTo(0f, 0f);
- path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
- path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
- return new PathInterpolator(path);
- }
-}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
index 3417ffd6b83a..142fd21d4a16 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
@@ -28,7 +28,7 @@ import android.util.MathUtils
import android.view.View
import android.view.ViewGroup
import android.view.animation.Interpolator
-import com.android.systemui.animation.Interpolators.LINEAR
+import com.android.app.animation.Interpolators.LINEAR
import kotlin.math.roundToInt
private const val TAG = "LaunchAnimator"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index 9e9929e79d47..9346a2f7ebb7 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -24,10 +24,30 @@ import android.graphics.Canvas
import android.graphics.Typeface
import android.graphics.fonts.Font
import android.text.Layout
+import android.text.TextPaint
+import android.util.LruCache
private const val DEFAULT_ANIMATION_DURATION: Long = 300
+private const val TYPEFACE_CACHE_MAX_ENTRIES = 5
typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
+
+interface TypefaceVariantCache {
+ fun getTypefaceForVariant(fvar: String, targetPaint: TextPaint): Typeface?
+}
+
+class TypefaceVariantCacheImpl() : TypefaceVariantCache {
+ private val cache = LruCache<String, Typeface>(TYPEFACE_CACHE_MAX_ENTRIES)
+ override fun getTypefaceForVariant(fvar: String, targetPaint: TextPaint): Typeface? {
+ cache.get(fvar)?.let {
+ return it
+ }
+
+ targetPaint.fontVariationSettings = fvar
+ return targetPaint.typeface?.also { cache.put(fvar, it) }
+ }
+}
+
/**
* This class provides text animation between two styles.
*
@@ -54,9 +74,19 @@ typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
* ```
* </code> </pre>
*/
-class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
+class TextAnimator(
+ layout: Layout,
+ private val invalidateCallback: () -> Unit,
+) {
+ var typefaceCache: TypefaceVariantCache = TypefaceVariantCacheImpl()
+ get() = field
+ set(value) {
+ field = value
+ textInterpolator.typefaceCache = value
+ }
+
// Following two members are for mutable for testing purposes.
- public var textInterpolator: TextInterpolator = TextInterpolator(layout)
+ public var textInterpolator: TextInterpolator = TextInterpolator(layout, typefaceCache)
public var animator: ValueAnimator =
ValueAnimator.ofFloat(1f).apply {
duration = DEFAULT_ANIMATION_DURATION
@@ -66,9 +96,7 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
}
addListener(
object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- textInterpolator.rebase()
- }
+ override fun onAnimationEnd(animation: Animator?) = textInterpolator.rebase()
override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase()
}
)
@@ -114,8 +142,6 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
private val fontVariationUtils = FontVariationUtils()
- private val typefaceCache = HashMap<String, Typeface?>()
-
fun updateLayout(layout: Layout) {
textInterpolator.layout = layout
}
@@ -219,11 +245,7 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
if (!fvar.isNullOrBlank()) {
textInterpolator.targetPaint.typeface =
- typefaceCache.getOrElse(fvar) {
- textInterpolator.targetPaint.fontVariationSettings = fvar
- typefaceCache.put(fvar, textInterpolator.targetPaint.typeface)
- textInterpolator.targetPaint.typeface
- }
+ typefaceCache.getTypefaceForVariant(fvar, textInterpolator.targetPaint)
}
if (color != null) {
@@ -302,7 +324,8 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
weight = weight,
width = width,
opticalSize = opticalSize,
- roundness = roundness,)
+ roundness = roundness,
+ )
setTextStyle(
fvar = fvar,
textSize = textSize,
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
index 23f16f2a3137..a041926bf5d2 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
@@ -28,8 +28,10 @@ import com.android.internal.graphics.ColorUtils
import java.lang.Math.max
/** Provide text style linear interpolation for plain text. */
-class TextInterpolator(layout: Layout) {
-
+class TextInterpolator(
+ layout: Layout,
+ var typefaceCache: TypefaceVariantCache,
+) {
/**
* Returns base paint used for interpolation.
*
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
index 58ffef25cb42..8e79e3ce1742 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
@@ -25,6 +25,7 @@ import android.util.IntProperty
import android.view.View
import android.view.ViewGroup
import android.view.animation.Interpolator
+import com.android.app.animation.Interpolators
import kotlin.math.max
import kotlin.math.min
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
index f3d8b1782486..dd32851a97cf 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt
@@ -19,7 +19,7 @@ package com.android.systemui.animation.back
import android.util.DisplayMetrics
import android.view.animation.Interpolator
import android.window.BackEvent
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.util.dpToPx
/** Used to convert [BackEvent] into a [BackTransformation]. */
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index 387b67d231cd..520c8882b428 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -40,7 +40,8 @@ class SystemUIIssueRegistry : IssueRegistry() {
SoftwareBitmapDetector.ISSUE,
NonInjectedServiceDetector.ISSUE,
StaticSettingsProviderDetector.ISSUE,
- DemotingTestWithoutBugDetector.ISSUE
+ DemotingTestWithoutBugDetector.ISSUE,
+ TestFunctionNameViolationDetector.ISSUE,
)
override val api: Int
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/TestFunctionNameViolationDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/TestFunctionNameViolationDetector.kt
new file mode 100644
index 000000000000..d91c7e53a7aa
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/TestFunctionNameViolationDetector.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.internal.systemui.lint
+
+import com.android.tools.lint.detector.api.AnnotationInfo
+import com.android.tools.lint.detector.api.AnnotationUsageInfo
+import com.android.tools.lint.detector.api.AnnotationUsageType
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.getParentOfType
+import org.jetbrains.uast.kotlin.KotlinUAnnotation
+import org.jetbrains.uast.kotlin.KotlinUMethod
+
+/**
+ * Detects test function naming violations regarding use of the backtick-wrapped space-allowed
+ * feature of Kotlin functions.
+ */
+class TestFunctionNameViolationDetector : Detector(), SourceCodeScanner {
+
+ override fun applicableAnnotations(): List<String> = listOf(ANNOTATION)
+ override fun isApplicableAnnotationUsage(type: AnnotationUsageType): Boolean = true
+
+ @Suppress("UnstableApiUsage")
+ override fun visitAnnotationUsage(
+ context: JavaContext,
+ element: UElement,
+ annotationInfo: AnnotationInfo,
+ usageInfo: AnnotationUsageInfo,
+ ) {
+ (element as? KotlinUAnnotation)?.getParentOfType(KotlinUMethod::class.java)?.let { method ->
+ if (method.name.contains(" ")) {
+ context.report(
+ issue = ISSUE,
+ scope = method.nameIdentifier,
+ location = context.getLocation(method.nameIdentifier),
+ message =
+ "Spaces are not allowed in test names. Use pascalCase_withUnderScores" +
+ " instead.",
+ )
+ }
+ }
+ }
+
+ companion object {
+ private const val ANNOTATION = "org.junit.Test"
+
+ @JvmStatic
+ val ISSUE =
+ Issue.create(
+ id = "TestFunctionNameViolation",
+ briefDescription = "Spaces not allowed in test function names.",
+ explanation =
+ """
+ We don't allow test function names because it leads to issues with our test
+ harness system (for example, see b/277739595). Please use
+ pascalCase_withUnderScores instead.
+ """,
+ category = Category.TESTING,
+ priority = 8,
+ severity = Severity.FATAL,
+ implementation =
+ Implementation(
+ TestFunctionNameViolationDetector::class.java,
+ Scope.JAVA_FILE_SCOPE,
+ ),
+ )
+ }
+}
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/TestFunctionNameViolationDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/TestFunctionNameViolationDetectorTest.kt
new file mode 100644
index 000000000000..db73154c02d9
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/TestFunctionNameViolationDetectorTest.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+@Suppress("UnstableApiUsage")
+class TestFunctionNameViolationDetectorTest : SystemUILintDetectorTest() {
+ override fun getDetector(): Detector {
+ return TestFunctionNameViolationDetector()
+ }
+
+ override fun getIssues(): List<Issue> {
+ return listOf(
+ TestFunctionNameViolationDetector.ISSUE,
+ )
+ }
+
+ @Test
+ fun violations() {
+ lint()
+ .files(
+ kotlin(
+ """
+ package test.pkg.name
+
+ import org.junit.Test
+
+ class MyTest {
+ @Test
+ fun `illegal test name - violation should be detected`() {
+ // some test code here.
+ }
+
+ @Test
+ fun legitimateTestName_doesNotViolate() {
+ // some test code here.
+ }
+
+ fun helperFunction_doesNotViolate() {
+ // some code.
+ }
+
+ fun `helper function - does not violate`() {
+ // some code.
+ }
+ }
+ """
+ .trimIndent()
+ ),
+ testAnnotationStub,
+ )
+ .issues(
+ TestFunctionNameViolationDetector.ISSUE,
+ )
+ .run()
+ .expectWarningCount(0)
+ .expect(
+ """
+ src/test/pkg/name/MyTest.kt:7: Error: Spaces are not allowed in test names. Use pascalCase_withUnderScores instead. [TestFunctionNameViolation]
+ fun `illegal test name - violation should be detected`() {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ )
+ }
+
+ companion object {
+ private val testAnnotationStub: TestFile =
+ kotlin(
+ """
+ package org.junit
+
+ import java.lang.annotation.ElementType
+ import java.lang.annotation.Retention
+ import java.lang.annotation.RetentionPolicy
+ import java.lang.annotation.Target
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.METHOD})
+ annotation class Test
+ """
+ )
+ }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 86bd5f2bff5a..3688f9e35e63 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -29,9 +29,9 @@ import android.text.format.DateFormat
import android.util.AttributeSet
import android.util.MathUtils
import android.widget.TextView
+import com.android.app.animation.Interpolators
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.animation.GlyphCallback
-import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.TextAnimator
import com.android.systemui.customization.R
import com.android.systemui.plugins.log.LogBuffer
@@ -648,7 +648,7 @@ class AnimatableClockView @JvmOverloads constructor(
private const val DIGITS_PER_LINE = 2
// How much of "fraction" to spend on canceling the animation, if needed
- private const val ANIMATION_CANCELLATION_TIME = 0.4f
+ private const val ANIMATION_CANCELLATION_TIME = 0f
// Delays. Each digit's animation should have a slight delay, so we get a nice
// "stepping" effect. When moving right, the second digit of the hour should move first.
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 6ca7f12e842b..3fda83d7b6f4 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -263,6 +263,13 @@ class DefaultClockController(
view.animateDoze(dozeState.isActive, !hasJumped)
}
}
+
+ override fun onPickerCarouselSwiping(swipingFraction: Float, previewRatio: Float) {
+ // TODO(b/278936436): refactor this part when we change recomputePadding
+ // when on the side, swipingFraction = 0, translationY should offset
+ // the top margin change in recomputePadding to make clock be centered
+ view.translationY = 0.5f * view.bottom * (1 - swipingFraction)
+ }
}
inner class LargeClockAnimations(
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index e0d01845562f..1811c02d549d 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -187,7 +187,7 @@ public interface BcSmartspaceDataPlugin extends Plugin {
if (action.getIntent() != null) {
startIntent(v, action.getIntent(), showOnLockscreen);
} else if (action.getPendingIntent() != null) {
- startPendingIntent(action.getPendingIntent(), showOnLockscreen);
+ startPendingIntent(v, action.getPendingIntent(), showOnLockscreen);
}
} catch (ActivityNotFoundException e) {
Log.w(TAG, "Could not launch intent for action: " + action, e);
@@ -199,7 +199,7 @@ public interface BcSmartspaceDataPlugin extends Plugin {
if (action.getIntent() != null) {
startIntent(v, action.getIntent(), showOnLockscreen);
} else if (action.getPendingIntent() != null) {
- startPendingIntent(action.getPendingIntent(), showOnLockscreen);
+ startPendingIntent(v, action.getPendingIntent(), showOnLockscreen);
}
} catch (ActivityNotFoundException e) {
Log.w(TAG, "Could not launch intent for action: " + action, e);
@@ -210,6 +210,6 @@ public interface BcSmartspaceDataPlugin extends Plugin {
void startIntent(View v, Intent i, boolean showOnLockscreen);
/** Start the PendingIntent */
- void startPendingIntent(PendingIntent pi, boolean showOnLockscreen);
+ void startPendingIntent(View v, PendingIntent pi, boolean showOnLockscreen);
}
}
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 64aa629b02f0..331307a0d9f4 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -44,6 +44,8 @@
<LinearLayout android:id="@+id/status_bar_contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingEnd="@dimen/status_bar_padding_end"
android:paddingTop="@dimen/status_bar_padding_top"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index bbac7b014064..4e68efeea870 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -404,7 +404,34 @@
<string name="biometric_dialog_last_pin_attempt_before_wipe_profile">If you enter an incorrect PIN on the next attempt, your work profile and its data will be deleted.</string>
<!-- Content of a dialog shown when the user only has one attempt left to provide the correct password before the work profile is removed. [CHAR LIMIT=NONE] -->
<string name="biometric_dialog_last_password_attempt_before_wipe_profile">If you enter an incorrect password on the next attempt, your work profile and its data will be deleted.</string>
-
+ <!-- Confirmation button label for a dialog shown when the system requires the user to re-enroll their biometrics. [CHAR LIMIT=20] -->
+ <string name="biometric_re_enroll_dialog_confirm">Set up</string>
+ <!-- Cancel button label for a dialog shown when the system requires the user to re-enroll their biometric. [CHAR LIMIT=20] -->
+ <string name="biometric_re_enroll_dialog_cancel">Not now</string>
+ <!-- Notification content shown when the system requires the user to re-enroll their biometrics. [CHAR LIMIT=NONE] -->
+ <string name="biometric_re_enroll_notification_content">This is required to improve security and performance</string>
+ <!-- Notification title shown when the system requires the user to re-enroll their fingerprint. [CHAR LIMIT=NONE] -->
+ <string name="fingerprint_re_enroll_notification_title">Set up Fingerprint Unlock again</string>
+ <!-- Name shown for system notifications related to the fingerprint unlock feature. [CHAR LIMIT=NONE] -->
+ <string name="fingerprint_re_enroll_notification_name">Fingerprint Unlock</string>
+ <!-- Title for a dialog shown when the system requires the user to re-enroll their fingerprint. [CHAR LIMIT=NONE] -->
+ <string name="fingerprint_re_enroll_dialog_title">Set up Fingerprint Unlock</string>
+ <!-- Content for a dialog shown when the system requires the user to re-enroll their fingerprint. [CHAR LIMIT=NONE] -->
+ <string name="fingerprint_re_enroll_dialog_content">To set up Fingerprint Unlock again, your current fingerprint images and models will be deleted.\n\nAfter they\’re deleted, you\’ll need to set up Fingerprint Unlock again to use your fingerprint to unlock your phone or verify it\’s you.</string>
+ <!-- Content for a dialog shown when the system requires the user to re-enroll their fingerprint (singular). [CHAR LIMIT=NONE] -->
+ <string name="fingerprint_re_enroll_dialog_content_singular">To set up Fingerprint Unlock again, your current fingerprint images and model will be deleted.\n\nAfter they\’re deleted, you\’ll need to set up Fingerprint Unlock again to use your fingerprint to unlock your phone or verify it\’s you.</string>
+ <!-- Content for a dialog shown when an error occurs while the user is trying to re-enroll their fingerprint. [CHAR LIMIT=NONE] -->
+ <string name="fingerprint_reenroll_failure_dialog_content">Couldn\u2019t set up fingerprint unlock. Go to Settings to try again.</string>
+ <!-- Notification title shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
+ <string name="face_re_enroll_notification_title">Set up Face Unlock again</string>
+ <!-- Name shown for system notifications related to the face unlock feature. [CHAR LIMIT=NONE] -->
+ <string name="face_re_enroll_notification_name">Face Unlock</string>
+ <!-- Title for a dialog shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
+ <string name="face_re_enroll_dialog_title">Set up Face Unlock</string>
+ <!-- Content for a dialog shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
+ <string name="face_re_enroll_dialog_content">To set up Face Unlock again, your current face model will be deleted.\n\nYou\’ll need to set up this feature again to use your face to unlock your phone.</string>
+ <!-- Content for a dialog shown when an error occurs while the user is trying to re-enroll their face. [CHAR LIMIT=NONE] -->
+ <string name="face_reenroll_failure_dialog_content">Couldn\u2019t set up face unlock. Go to Settings to try again.</string>
<!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
<string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
<!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
index f59bf8e766fe..64d766dcd488 100644
--- a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
+++ b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
@@ -26,8 +26,8 @@ import android.content.res.TypedArray
import android.graphics.Color
import android.util.AttributeSet
import android.view.View
+import com.android.app.animation.Interpolators
import com.android.settingslib.Utils
-import com.android.systemui.animation.Interpolators
/** Displays security messages for the keyguard bouncer. */
open class BouncerKeyguardMessageArea(context: Context?, attrs: AttributeSet?) :
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index a6c782d88e18..a30cae950dfe 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -15,9 +15,9 @@ import android.widget.RelativeLayout;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
+import com.android.app.animation.Interpolators;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.plugins.log.LogBuffer;
import com.android.systemui.plugins.log.LogLevel;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 03947542d21e..09820305f34e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -32,9 +32,9 @@ import android.view.animation.Interpolator;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
+import com.android.app.animation.Interpolators;
import com.android.settingslib.animation.DisappearAnimationUtils;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 33bea027cd20..1d7c35d0c90d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -45,11 +45,11 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.TextViewInputDisabler;
import com.android.systemui.DejankUtils;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
/**
* Displays an alphanumeric (latin-1) key entry for the user to enter
* an unlock password
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 0a91150e6c39..b4ddc9a975c2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -34,9 +34,9 @@ import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
+import com.android.app.animation.Interpolators;
import com.android.internal.widget.LockscreenCredential;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import java.util.ArrayList;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index ba5a8c94dc23..78021ad21a77 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -32,7 +32,7 @@ import static androidx.constraintlayout.widget.ConstraintSet.START;
import static androidx.constraintlayout.widget.ConstraintSet.TOP;
import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT;
-import static com.android.systemui.animation.InterpolatorsAndroidX.DECELERATE_QUINT;
+import static com.android.app.animation.InterpolatorsAndroidX.DECELERATE_QUINT;
import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
import static java.lang.Integer.max;
@@ -86,6 +86,7 @@ import androidx.constraintlayout.widget.ConstraintSet;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
+import com.android.app.animation.Interpolators;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -96,7 +97,6 @@ import com.android.settingslib.Utils;
import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.system.SysUiStatsLog;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
index c9128e5881cc..96ac8ad56651 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
@@ -26,9 +26,9 @@ import android.util.MathUtils
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
+import com.android.app.animation.Interpolators
import com.android.internal.R.interpolator.fast_out_extra_slow_in
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
/** Animates constraint layout changes for the security view. */
class KeyguardSecurityViewTransition : Transition() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 65a71664e245..b4f124aa598a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -44,11 +44,11 @@ import androidx.slice.core.SliceQuery;
import androidx.slice.widget.RowContent;
import androidx.slice.widget.SliceContent;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.util.wakelock.KeepAwakeAnimationListener;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index edfcb8d0d1a6..89e7e17a7ce1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -40,11 +40,11 @@ import androidx.annotation.VisibleForTesting;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
+import com.android.app.animation.Interpolators;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.keyguard.KeyguardClockSwitch.ClockSize;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ClockController;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 10c08bc3e8b3..9573913e5e2f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1832,7 +1832,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
} else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED,
- getSendingUserId()));
+ getSendingUserId(), 0));
} else if (ACTION_USER_UNLOCKED.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_UNLOCKED,
getSendingUserId(), 0));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 651c9796140e..aa652fabb294 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -21,7 +21,7 @@ import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import android.util.Property;
import android.view.View;
-import com.android.systemui.animation.Interpolators;
+import com.android.app.animation.Interpolators;
import com.android.systemui.plugins.log.LogBuffer;
import com.android.systemui.plugins.log.LogLevel;
import com.android.systemui.statusbar.StatusBarState;
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index ad669099284f..e76112366e9e 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -30,7 +30,7 @@ import android.widget.TextView;
import androidx.annotation.StyleRes;
-import com.android.systemui.animation.Interpolators;
+import com.android.app.animation.Interpolators;
/**
* Provides background color and radius animations for key pad buttons.
diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java
index 14810d9baf02..c4ecb393751c 100644
--- a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java
@@ -35,9 +35,9 @@ import android.widget.LinearLayout;
import androidx.core.graphics.drawable.DrawableCompat;
+import com.android.app.animation.Interpolators;
import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
/**
* This class contains implementation for methods that will be used when user has set a
diff --git a/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java b/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java
index 12dd8f06de17..4c16d41c8672 100644
--- a/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java
@@ -16,6 +16,7 @@ package com.android.systemui;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.util.AttributeSet;
@@ -23,21 +24,29 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* Custom {@link FrameLayout} that re-inflates when changes to {@link Configuration} happen.
* Currently supports changes to density, asset path, and locale.
*/
-public class AutoReinflateContainer extends FrameLayout implements
- ConfigurationController.ConfigurationListener {
+public class AutoReinflateContainer extends FrameLayout {
+
+ private static final Set<Integer> SUPPORTED_CHANGES = Set.of(
+ ActivityInfo.CONFIG_LOCALE,
+ ActivityInfo.CONFIG_UI_MODE,
+ ActivityInfo.CONFIG_ASSETS_PATHS,
+ ActivityInfo.CONFIG_DENSITY,
+ ActivityInfo.CONFIG_FONT_SCALE
+ );
private final List<InflateListener> mInflateListeners = new ArrayList<>();
private final int mLayout;
+ private final Configuration mLastConfig = new Configuration();
+
public AutoReinflateContainer(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -51,15 +60,14 @@ public class AutoReinflateContainer extends FrameLayout implements
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- Dependency.get(ConfigurationController.class).addCallback(this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- Dependency.get(ConfigurationController.class).removeCallback(this);
+ protected void onConfigurationChanged(Configuration newConfig) {
+ int diff = mLastConfig.updateFrom(newConfig);
+ for (int change: SUPPORTED_CHANGES) {
+ if ((diff & change) != 0) {
+ inflateLayout();
+ return;
+ }
+ }
}
protected void inflateLayoutImpl() {
@@ -80,26 +88,6 @@ public class AutoReinflateContainer extends FrameLayout implements
listener.onInflated(getChildAt(0));
}
- @Override
- public void onDensityOrFontScaleChanged() {
- inflateLayout();
- }
-
- @Override
- public void onThemeChanged() {
- inflateLayout();
- }
-
- @Override
- public void onUiModeChanged() {
- inflateLayout();
- }
-
- @Override
- public void onLocaleListChanged() {
- inflateLayout();
- }
-
public interface InflateListener {
/**
* Called whenever a new view is inflated.
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index ef16a3a3c63d..aade71a83c28 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -106,7 +106,6 @@ import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
@@ -134,14 +133,14 @@ import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import dagger.Lazy;
+
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Named;
-import dagger.Lazy;
-
/**
* Class to handle ugly dependencies throughout sysui until we determine the
* long-term dependency injection solution.
@@ -270,7 +269,6 @@ public class Dependency {
@Inject Lazy<NotificationShadeWindowController> mNotificationShadeWindowController;
@Inject Lazy<StatusBarWindowController> mTempStatusBarWindowController;
@Inject Lazy<DarkIconDispatcher> mDarkIconDispatcher;
- @Inject Lazy<ConfigurationController> mConfigurationController;
@Inject Lazy<StatusBarIconController> mStatusBarIconController;
@Inject Lazy<ScreenLifecycle> mScreenLifecycle;
@Inject Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
@@ -441,8 +439,6 @@ public class Dependency {
mProviders.put(DarkIconDispatcher.class, mDarkIconDispatcher::get);
- mProviders.put(ConfigurationController.class, mConfigurationController::get);
-
mProviders.put(StatusBarIconController.class, mStatusBarIconController::get);
mProviders.put(ScreenLifecycle.class, mScreenLifecycle::get);
diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
index de82ca014722..c1871e09a791 100644
--- a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
+++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
@@ -36,7 +36,7 @@ import android.view.Surface
import android.view.View
import androidx.annotation.VisibleForTesting
import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.util.asIndenting
import java.io.PrintWriter
diff --git a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
index a3e7d71a92f6..e72ad820c410 100644
--- a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
@@ -34,7 +34,7 @@ import androidx.core.graphics.ColorUtils
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.biometrics.AuthController
import com.android.systemui.log.ScreenDecorationsLogger
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 2503520ba1d9..9adfcc9a2054 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -43,8 +43,8 @@ import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.VisibleForTesting;
+import com.android.app.animation.Interpolators;
import com.android.internal.dynamicanimation.animation.SpringForce;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 6e8275f64eea..7bfd84e4b647 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -346,6 +346,15 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest
setSystemGestureExclusion();
mIsVisible = true;
mCallback.onSettingsPanelVisibilityChanged(/* shown= */ true);
+
+ if (resetPosition) {
+ // We could not put focus on the settings panel automatically
+ // since it is an inactive window. Therefore, we announce the existence of
+ // magnification settings for accessibility when it is opened.
+ mSettingView.announceForAccessibility(
+ mContext.getResources().getString(
+ R.string.accessibility_magnification_settings_panel_description));
+ }
}
mContext.registerReceiver(mScreenOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
index d6f0b59accb1..d49197557dc4 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
@@ -32,8 +32,8 @@ import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
+import com.android.app.animation.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
/**
* Visually discloses that contextual data was provided to an assistant.
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
index 0002ae95f476..2aac056b0bde 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -45,9 +45,9 @@ import android.widget.TextView;
import androidx.annotation.StyleRes;
import androidx.annotation.VisibleForTesting;
+import com.android.app.animation.Interpolators;
import com.android.systemui.DualToneHandler;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.policy.BatteryController;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index aeebb010eb1e..be585ed133d7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -59,11 +59,11 @@ import android.widget.ScrollView;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.biometrics.AuthController.ScaleFactorProvider;
import com.android.systemui.biometrics.domain.interactor.BiometricPromptCredentialInteractor;
import com.android.systemui.biometrics.ui.CredentialView;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index e0b9f01bf662..782a10bd7e4c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -23,14 +23,17 @@ import android.content.Context
import android.graphics.Point
import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.biometrics.BiometricSourceType
+import android.util.DisplayMetrics
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.logging.KeyguardLogger
import com.android.settingslib.Utils
import com.android.settingslib.udfps.UdfpsOverlayParams
+import com.android.systemui.CoreStartable
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.WakefulnessLifecycle
@@ -39,12 +42,11 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LiftReveal
import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.phone.BiometricUnlockController
-import com.android.systemui.statusbar.phone.CentralSurfaces
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.ViewController
@@ -60,9 +62,8 @@ import javax.inject.Provider
*
* The ripple uses the accent color of the current theme.
*/
-@CentralSurfacesScope
+@SysUISingleton
class AuthRippleController @Inject constructor(
- private val centralSurfaces: CentralSurfaces,
private val sysuiContext: Context,
private val authController: AuthController,
private val configurationController: ConfigurationController,
@@ -73,12 +74,15 @@ class AuthRippleController @Inject constructor(
private val notificationShadeWindowController: NotificationShadeWindowController,
private val udfpsControllerProvider: Provider<UdfpsController>,
private val statusBarStateController: StatusBarStateController,
+ private val displayMetrics: DisplayMetrics,
private val featureFlags: FeatureFlags,
private val logger: KeyguardLogger,
private val biometricUnlockController: BiometricUnlockController,
+ private val lightRevealScrim: LightRevealScrim,
rippleView: AuthRippleView?
) :
ViewController<AuthRippleView>(rippleView),
+ CoreStartable,
KeyguardStateController.Callback,
WakefulnessLifecycle.Observer {
@@ -92,6 +96,10 @@ class AuthRippleController @Inject constructor(
private var udfpsController: UdfpsController? = null
private var udfpsRadius: Float = -1f
+ override fun start() {
+ init()
+ }
+
@VisibleForTesting
public override fun onViewAttached() {
authController.addCallback(authControllerCallback)
@@ -153,8 +161,8 @@ class AuthRippleController @Inject constructor(
it.y,
0,
Math.max(
- Math.max(it.x, centralSurfaces.displayWidth.toInt() - it.x),
- Math.max(it.y, centralSurfaces.displayHeight.toInt() - it.y)
+ Math.max(it.x, displayMetrics.widthPixels - it.x),
+ Math.max(it.y, displayMetrics.heightPixels - it.y)
)
)
logger.showingUnlockRippleAt(it.x, it.y, "FP sensor radius: $udfpsRadius")
@@ -168,8 +176,8 @@ class AuthRippleController @Inject constructor(
it.y,
0,
Math.max(
- Math.max(it.x, centralSurfaces.displayWidth.toInt() - it.x),
- Math.max(it.y, centralSurfaces.displayHeight.toInt() - it.y)
+ Math.max(it.x, displayMetrics.widthPixels - it.x),
+ Math.max(it.y, displayMetrics.heightPixels - it.y)
)
)
logger.showingUnlockRippleAt(it.x, it.y, "Face unlock ripple")
@@ -184,11 +192,10 @@ class AuthRippleController @Inject constructor(
// This code path is not used if the KeyguardTransitionRepository is managing the light
// reveal scrim.
if (!featureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
- val lightRevealScrim = centralSurfaces.lightRevealScrim
if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
circleReveal?.let {
- lightRevealScrim?.revealAmount = 0f
- lightRevealScrim?.revealEffect = it
+ lightRevealScrim.revealAmount = 0f
+ lightRevealScrim.revealEffect = it
startLightRevealScrimOnKeyguardFadingAway = true
}
}
@@ -208,8 +215,7 @@ class AuthRippleController @Inject constructor(
}
if (keyguardStateController.isKeyguardFadingAway) {
- val lightRevealScrim = centralSurfaces.lightRevealScrim
- if (startLightRevealScrimOnKeyguardFadingAway && lightRevealScrim != null) {
+ if (startLightRevealScrimOnKeyguardFadingAway) {
lightRevealScrimAnimator?.cancel()
lightRevealScrimAnimator = ValueAnimator.ofFloat(.1f, 1f).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index b0071340cf1a..5ede16d221b7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -28,7 +28,7 @@ import android.util.AttributeSet
import android.view.View
import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.surfaceeffects.ripple.RippleShader
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java
new file mode 100644
index 000000000000..c22a66b210cb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationBroadcastReceiver.java
@@ -0,0 +1,72 @@
+/*
+ * 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.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.biometrics.BiometricSourceType;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+import javax.inject.Inject;
+
+/**
+ * Receives broadcasts sent by {@link BiometricNotificationService} and takes
+ * the appropriate action.
+ */
+@SysUISingleton
+public class BiometricNotificationBroadcastReceiver extends BroadcastReceiver {
+ static final String ACTION_SHOW_FACE_REENROLL_DIALOG = "face_action_show_reenroll_dialog";
+ static final String ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG =
+ "fingerprint_action_show_reenroll_dialog";
+
+ private static final String TAG = "BiometricNotificationBroadcastReceiver";
+
+ private final Context mContext;
+ private final BiometricNotificationDialogFactory mNotificationDialogFactory;
+ @Inject
+ BiometricNotificationBroadcastReceiver(Context context,
+ BiometricNotificationDialogFactory notificationDialogFactory) {
+ mContext = context;
+ mNotificationDialogFactory = notificationDialogFactory;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+
+ switch (action) {
+ case ACTION_SHOW_FACE_REENROLL_DIALOG:
+ mNotificationDialogFactory.createReenrollDialog(mContext,
+ new SystemUIDialog(mContext),
+ BiometricSourceType.FACE)
+ .show();
+ break;
+ case ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG:
+ mNotificationDialogFactory.createReenrollDialog(
+ mContext,
+ new SystemUIDialog(mContext),
+ BiometricSourceType.FINGERPRINT)
+ .show();
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationDialogFactory.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationDialogFactory.java
new file mode 100644
index 000000000000..3e6508c6da70
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationDialogFactory.java
@@ -0,0 +1,177 @@
+/*
+ * 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.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.face.Face;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+import javax.inject.Inject;
+
+/**
+ * Manages the creation of dialogs to be shown for biometric re enroll notifications.
+ */
+@SysUISingleton
+public class BiometricNotificationDialogFactory {
+ private static final String TAG = "BiometricNotificationDialogFactory";
+
+ @Inject
+ BiometricNotificationDialogFactory() {}
+
+ Dialog createReenrollDialog(final Context context, final SystemUIDialog sysuiDialog,
+ BiometricSourceType biometricSourceType) {
+ if (biometricSourceType == BiometricSourceType.FACE) {
+ sysuiDialog.setTitle(context.getString(R.string.face_re_enroll_dialog_title));
+ sysuiDialog.setMessage(context.getString(R.string.face_re_enroll_dialog_content));
+ } else if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
+ FingerprintManager fingerprintManager = context.getSystemService(
+ FingerprintManager.class);
+ sysuiDialog.setTitle(context.getString(R.string.fingerprint_re_enroll_dialog_title));
+ if (fingerprintManager.getEnrolledFingerprints().size() == 1) {
+ sysuiDialog.setMessage(context.getString(
+ R.string.fingerprint_re_enroll_dialog_content_singular));
+ } else {
+ sysuiDialog.setMessage(context.getString(
+ R.string.fingerprint_re_enroll_dialog_content));
+ }
+ }
+
+ sysuiDialog.setPositiveButton(R.string.biometric_re_enroll_dialog_confirm,
+ (dialog, which) -> onReenrollDialogConfirm(context, biometricSourceType));
+ sysuiDialog.setNegativeButton(R.string.biometric_re_enroll_dialog_cancel,
+ (dialog, which) -> {});
+ return sysuiDialog;
+ }
+
+ private static Dialog createReenrollFailureDialog(Context context,
+ BiometricSourceType biometricType) {
+ final SystemUIDialog sysuiDialog = new SystemUIDialog(context);
+
+ if (biometricType == BiometricSourceType.FACE) {
+ sysuiDialog.setMessage(context.getString(
+ R.string.face_reenroll_failure_dialog_content));
+ } else if (biometricType == BiometricSourceType.FINGERPRINT) {
+ sysuiDialog.setMessage(context.getString(
+ R.string.fingerprint_reenroll_failure_dialog_content));
+ }
+
+ sysuiDialog.setPositiveButton(R.string.ok, (dialog, which) -> {});
+ return sysuiDialog;
+ }
+
+ private static void onReenrollDialogConfirm(final Context context,
+ BiometricSourceType biometricType) {
+ if (biometricType == BiometricSourceType.FACE) {
+ reenrollFace(context);
+ } else if (biometricType == BiometricSourceType.FINGERPRINT) {
+ reenrollFingerprint(context);
+ }
+ }
+
+ private static void reenrollFingerprint(Context context) {
+ FingerprintManager fingerprintManager = context.getSystemService(FingerprintManager.class);
+ if (fingerprintManager == null) {
+ Log.e(TAG, "Not launching enrollment. Fingerprint manager was null!");
+ createReenrollFailureDialog(context, BiometricSourceType.FINGERPRINT).show();
+ return;
+ }
+
+ if (!fingerprintManager.hasEnrolledTemplates(context.getUserId())) {
+ createReenrollFailureDialog(context, BiometricSourceType.FINGERPRINT).show();
+ return;
+ }
+
+ // Remove all enrolled fingerprint. Launch enrollment if successful.
+ fingerprintManager.removeAll(context.getUserId(),
+ new FingerprintManager.RemovalCallback() {
+ boolean mDidShowFailureDialog;
+
+ @Override
+ public void onRemovalError(Fingerprint fingerprint, int errMsgId,
+ CharSequence errString) {
+ Log.e(TAG, "Not launching enrollment."
+ + "Failed to remove existing face(s).");
+ if (!mDidShowFailureDialog) {
+ mDidShowFailureDialog = true;
+ createReenrollFailureDialog(context, BiometricSourceType.FINGERPRINT)
+ .show();
+ }
+ }
+
+ @Override
+ public void onRemovalSucceeded(Fingerprint fingerprint, int remaining) {
+ if (!mDidShowFailureDialog && remaining == 0) {
+ Intent intent = new Intent(Settings.ACTION_FINGERPRINT_ENROLL);
+ intent.setPackage("com.android.settings");
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+ }
+ });
+ }
+
+ private static void reenrollFace(Context context) {
+ FaceManager faceManager = context.getSystemService(FaceManager.class);
+ if (faceManager == null) {
+ Log.e(TAG, "Not launching enrollment. Face manager was null!");
+ createReenrollFailureDialog(context, BiometricSourceType.FACE).show();
+ return;
+ }
+
+ if (!faceManager.hasEnrolledTemplates(context.getUserId())) {
+ createReenrollFailureDialog(context, BiometricSourceType.FACE).show();
+ return;
+ }
+
+ // Remove all enrolled faces. Launch enrollment if successful.
+ faceManager.removeAll(context.getUserId(),
+ new FaceManager.RemovalCallback() {
+ boolean mDidShowFailureDialog;
+
+ @Override
+ public void onRemovalError(Face face, int errMsgId, CharSequence errString) {
+ Log.e(TAG, "Not launching enrollment."
+ + "Failed to remove existing face(s).");
+ if (!mDidShowFailureDialog) {
+ mDidShowFailureDialog = true;
+ createReenrollFailureDialog(context, BiometricSourceType.FACE).show();
+ }
+ }
+
+ @Override
+ public void onRemovalSucceeded(Face face, int remaining) {
+ if (!mDidShowFailureDialog && remaining == 0) {
+ Intent intent = new Intent("android.settings.FACE_ENROLL");
+ intent.setPackage("com.android.settings");
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+ }
+ });
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java
new file mode 100644
index 000000000000..4b17be3c45d4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java
@@ -0,0 +1,206 @@
+/*
+ * 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 static android.app.PendingIntent.FLAG_IMMUTABLE;
+
+import static com.android.systemui.biometrics.BiometricNotificationBroadcastReceiver.ACTION_SHOW_FACE_REENROLL_DIALOG;
+import static com.android.systemui.biometrics.BiometricNotificationBroadcastReceiver.ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricSourceType;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.CoreStartable;
+import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import javax.inject.Inject;
+
+/**
+ * Handles showing system notifications related to biometric unlock.
+ */
+@SysUISingleton
+public class BiometricNotificationService implements CoreStartable {
+
+ private static final String TAG = "BiometricNotificationService";
+ private static final String CHANNEL_ID = "BiometricHiPriNotificationChannel";
+ private static final String CHANNEL_NAME = " Biometric Unlock";
+ private static final int FACE_NOTIFICATION_ID = 1;
+ private static final int FINGERPRINT_NOTIFICATION_ID = 2;
+ private static final long SHOW_NOTIFICATION_DELAY_MS = 5_000L; // 5 seconds
+ private static final int REENROLL_REQUIRED = 1;
+ private static final int REENROLL_NOT_REQUIRED = 0;
+
+ private final Context mContext;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final KeyguardStateController mKeyguardStateController;
+ private final Handler mHandler;
+ private final NotificationManager mNotificationManager;
+ private final BiometricNotificationBroadcastReceiver mBroadcastReceiver;
+ private NotificationChannel mNotificationChannel;
+ private boolean mFaceNotificationQueued;
+ private boolean mFingerprintNotificationQueued;
+ private boolean mFingerprintReenrollRequired;
+
+ private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
+ new KeyguardStateController.Callback() {
+ private boolean mIsShowing = true;
+ @Override
+ public void onKeyguardShowingChanged() {
+ if (mKeyguardStateController.isShowing()
+ || mKeyguardStateController.isShowing() == mIsShowing) {
+ mIsShowing = mKeyguardStateController.isShowing();
+ return;
+ }
+ mIsShowing = mKeyguardStateController.isShowing();
+ if (isFaceReenrollRequired(mContext) && !mFaceNotificationQueued) {
+ queueFaceReenrollNotification();
+ }
+ if (mFingerprintReenrollRequired && !mFingerprintNotificationQueued) {
+ mFingerprintReenrollRequired = false;
+ queueFingerprintReenrollNotification();
+ }
+ }
+ };
+
+ private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onBiometricError(int msgId, String errString,
+ BiometricSourceType biometricSourceType) {
+ if (msgId == BiometricFaceConstants.BIOMETRIC_ERROR_RE_ENROLL
+ && biometricSourceType == BiometricSourceType.FACE) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_RE_ENROLL, REENROLL_REQUIRED,
+ UserHandle.USER_CURRENT);
+ } else if (msgId == BiometricFingerprintConstants.BIOMETRIC_ERROR_RE_ENROLL
+ && biometricSourceType == BiometricSourceType.FINGERPRINT) {
+ mFingerprintReenrollRequired = true;
+ }
+ }
+ };
+
+
+ @Inject
+ public BiometricNotificationService(Context context,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardStateController keyguardStateController,
+ Handler handler, NotificationManager notificationManager,
+ BiometricNotificationBroadcastReceiver biometricNotificationBroadcastReceiver) {
+ mContext = context;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mKeyguardStateController = keyguardStateController;
+ mHandler = handler;
+ mNotificationManager = notificationManager;
+ mBroadcastReceiver = biometricNotificationBroadcastReceiver;
+ }
+
+ @Override
+ public void start() {
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+ mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
+ mNotificationChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_HIGH);
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG);
+ intentFilter.addAction(ACTION_SHOW_FACE_REENROLL_DIALOG);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
+ }
+
+ private void queueFaceReenrollNotification() {
+ mFaceNotificationQueued = true;
+ final String title = mContext.getString(R.string.face_re_enroll_notification_title);
+ final String content = mContext.getString(
+ R.string.biometric_re_enroll_notification_content);
+ final String name = mContext.getString(R.string.face_re_enroll_notification_name);
+ mHandler.postDelayed(
+ () -> showNotification(ACTION_SHOW_FACE_REENROLL_DIALOG, title, content, name,
+ FACE_NOTIFICATION_ID),
+ SHOW_NOTIFICATION_DELAY_MS);
+ }
+
+ private void queueFingerprintReenrollNotification() {
+ mFingerprintNotificationQueued = true;
+ final String title = mContext.getString(R.string.fingerprint_re_enroll_notification_title);
+ final String content = mContext.getString(
+ R.string.biometric_re_enroll_notification_content);
+ final String name = mContext.getString(R.string.fingerprint_re_enroll_notification_name);
+ mHandler.postDelayed(
+ () -> showNotification(ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG, title, content,
+ name, FINGERPRINT_NOTIFICATION_ID),
+ SHOW_NOTIFICATION_DELAY_MS);
+ }
+
+ private void showNotification(String action, CharSequence title, CharSequence content,
+ CharSequence name, int notificationId) {
+ if (notificationId == FACE_NOTIFICATION_ID) {
+ mFaceNotificationQueued = false;
+ } else if (notificationId == FINGERPRINT_NOTIFICATION_ID) {
+ mFingerprintNotificationQueued = false;
+ }
+
+ if (mNotificationManager == null) {
+ Log.e(TAG, "Failed to show notification "
+ + action + ". Notification manager is null!");
+ return;
+ }
+
+ final Intent onClickIntent = new Intent(action);
+ final PendingIntent onClickPendingIntent = PendingIntent.getBroadcastAsUser(mContext,
+ 0 /* requestCode */, onClickIntent, FLAG_IMMUTABLE, UserHandle.CURRENT);
+
+ final Notification notification = new Notification.Builder(mContext, CHANNEL_ID)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setSmallIcon(com.android.internal.R.drawable.ic_lock)
+ .setContentTitle(title)
+ .setContentText(content)
+ .setSubText(name)
+ .setContentIntent(onClickPendingIntent)
+ .setAutoCancel(true)
+ .setLocalOnly(true)
+ .setOnlyAlertOnce(true)
+ .setVisibility(Notification.VISIBILITY_SECRET)
+ .build();
+
+ mNotificationManager.createNotificationChannel(mNotificationChannel);
+ mNotificationManager.notifyAsUser(TAG, notificationId, notification, UserHandle.CURRENT);
+ }
+
+ private boolean isFaceReenrollRequired(Context context) {
+ final int settingValue =
+ Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_RE_ENROLL, REENROLL_NOT_REQUIRED,
+ UserHandle.USER_CURRENT);
+ return settingValue == REENROLL_REQUIRED;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index ef7dcb7aac93..1dbafc6519f0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -19,7 +19,7 @@ import android.animation.ValueAnimator
import android.graphics.PointF
import android.graphics.RectF
import com.android.systemui.Dumpable
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionListener
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index ba8e60a08a1d..52db4ab8b593 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -40,9 +40,9 @@ 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.R;
-import com.android.systemui.animation.Interpolators;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieProperty;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
index 3b50bbcd9251..eaab75af4a51 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
@@ -23,11 +23,11 @@ import android.view.MotionEvent
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.R
import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.animation.Interpolators
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
index e2d36dc6abe1..9292bd7ecd60 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
@@ -6,8 +6,8 @@ import android.widget.ImageView
import android.widget.TextView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.animation.Interpolators
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
import com.android.systemui.biometrics.AuthDialog
import com.android.systemui.biometrics.AuthPanelController
import com.android.systemui.biometrics.ui.CredentialPasswordView
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 11ef749573b7..7bf8f4dac1fb 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -30,9 +30,9 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.app.animation.Interpolators;
import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.surfaceeffects.ripple.RippleShader;
import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
index 1fa9ac574c7e..1ffbe3230bda 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
@@ -72,7 +72,7 @@ public class EditTextActivity extends Activity
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Package not found: " + mClipboardManager.getPrimaryClipSource(), e);
}
- mEditText.setText(clip.getItemAt(0).getText());
+ mEditText.setText(clip.getItemAt(0).getText().toString());
mEditText.requestFocus();
mEditText.setSelection(0);
mSensitive = clip.getDescription().getExtras() != null
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
index 8d0edf829416..b447d66c08dd 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
@@ -32,7 +32,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.controls.ui.ControlsUiController
object ControlsAnimations {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 6a9aaf865251..e6361f46c8ad 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -50,7 +50,7 @@ import androidx.annotation.ColorInt
import androidx.annotation.VisibleForTesting
import com.android.internal.graphics.ColorUtils
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.util.concurrency.DelayableExecutor
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index fa36eee7c644..1461135d3d3b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -38,7 +38,7 @@ import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL
import java.util.IllegalFormatException
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 0be3bb69d136..0dcba50df4ca 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -50,6 +50,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
+import android.graphics.Color;
import android.hardware.SensorManager;
import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricManager;
@@ -113,13 +114,13 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.TestHarness;
import com.android.systemui.shared.system.PackageManagerWrapper;
+import dagger.Module;
+import dagger.Provides;
+
import java.util.Optional;
import javax.inject.Singleton;
-import dagger.Module;
-import dagger.Provides;
-
/**
* Provides Non-SystemUI, Framework-Owned instances to the dependency graph.
*/
@@ -323,7 +324,9 @@ public class FrameworkServicesModule {
@Provides
@Singleton
static InteractionJankMonitor provideInteractionJankMonitor() {
- return InteractionJankMonitor.getInstance();
+ InteractionJankMonitor jankMonitor = InteractionJankMonitor.getInstance();
+ jankMonitor.configDebugOverlay(Color.YELLOW, 0.75);
+ return jankMonitor;
}
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index de84cc22e0e1..5d6479ef4532 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -25,6 +25,7 @@ import com.android.systemui.SliceBroadcastRelayHandler
import com.android.systemui.accessibility.SystemActions
import com.android.systemui.accessibility.WindowMagnification
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.BiometricNotificationService
import com.android.systemui.clipboardoverlay.ClipboardListener
import com.android.systemui.controls.dagger.StartControlsStartableModule
import com.android.systemui.dagger.qualifiers.PerUser
@@ -75,6 +76,14 @@ abstract class SystemUICoreStartableModule {
@ClassKey(AuthController::class)
abstract fun bindAuthController(service: AuthController): CoreStartable
+ /** Inject into BiometricNotificationService */
+ @Binds
+ @IntoMap
+ @ClassKey(BiometricNotificationService::class)
+ abstract fun bindBiometricNotificationService(
+ service: BiometricNotificationService
+ ): CoreStartable
+
/** Inject into ChooserCoreStartable. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index cee829465047..dff2c0e91f1f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -288,7 +288,7 @@ public abstract class SystemUIModule {
INotificationManager notificationManager,
IDreamManager dreamManager,
NotificationVisibilityProvider visibilityProvider,
- NotificationInterruptStateProvider interruptionStateProvider,
+ VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
CommonNotifCollection notifCollection,
@@ -306,7 +306,7 @@ public abstract class SystemUIModule {
notificationManager,
dreamManager,
visibilityProvider,
- interruptionStateProvider,
+ visualInterruptionDecisionProvider,
zenModeController,
notifUserManager,
notifCollection,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index 5b56c04ae8aa..83f39b522f09 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -24,8 +24,8 @@ import android.view.animation.Interpolator
import androidx.core.animation.doOnEnd
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.animation.Interpolators
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
import com.android.systemui.complication.ComplicationHostViewController
import com.android.systemui.complication.ComplicationLayoutParams
import com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 15a32d21213f..c22019e96d74 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -33,9 +33,9 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
+import com.android.app.animation.Interpolators;
import com.android.dream.lowlight.LowLightTransitionCoordinator;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.complication.ComplicationHostViewController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
index 5bbfbda82944..3ef19b760826 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/conditions/DreamCondition.java
@@ -16,12 +16,9 @@
package com.android.systemui.dreams.conditions;
import android.app.DreamManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.text.TextUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.shared.condition.Condition;
import javax.inject.Inject;
@@ -30,48 +27,33 @@ import javax.inject.Inject;
* {@link DreamCondition} provides a signal when a dream begins and ends.
*/
public class DreamCondition extends Condition {
- private final Context mContext;
private final DreamManager mDreamManager;
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- processIntent(intent);
- }
- };
+ private final KeyguardUpdateMonitor mUpdateMonitor;
+
+
+ private final KeyguardUpdateMonitorCallback mUpdateCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onDreamingStateChanged(boolean dreaming) {
+ updateCondition(dreaming);
+ }
+ };
@Inject
- public DreamCondition(Context context,
- DreamManager dreamManager) {
- mContext = context;
+ public DreamCondition(DreamManager dreamManager, KeyguardUpdateMonitor monitor) {
mDreamManager = dreamManager;
- }
-
- private void processIntent(Intent intent) {
- // In the case of a non-existent sticky broadcast, ignore when there is no intent.
- if (intent == null) {
- return;
- }
- if (TextUtils.equals(intent.getAction(), Intent.ACTION_DREAMING_STARTED)) {
- updateCondition(true);
- } else if (TextUtils.equals(intent.getAction(), Intent.ACTION_DREAMING_STOPPED)) {
- updateCondition(false);
- } else {
- throw new IllegalStateException("unexpected intent:" + intent);
- }
+ mUpdateMonitor = monitor;
}
@Override
protected void start() {
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_DREAMING_STARTED);
- filter.addAction(Intent.ACTION_DREAMING_STOPPED);
- mContext.registerReceiver(mReceiver, filter);
+ mUpdateMonitor.registerCallback(mUpdateCallback);
updateCondition(mDreamManager.isDreaming());
}
@Override
protected void stop() {
- mContext.unregisterReceiver(mReceiver);
+ mUpdateMonitor.removeCallback(mUpdateCallback);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index aaf4358494f0..2ecb0a0043a2 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -87,7 +87,9 @@ object Flags {
releasedFlag(270682168, "animated_notification_shade_insets")
// TODO(b/268005230): Tracking Bug
- @JvmField val SENSITIVE_REVEAL_ANIM = unreleasedFlag(268005230, "sensitive_reveal_anim")
+ @JvmField
+ val SENSITIVE_REVEAL_ANIM =
+ unreleasedFlag(268005230, "sensitive_reveal_anim", teamfood = true)
// 200 - keyguard/lockscreen
// ** Flag retired **
@@ -108,7 +110,7 @@ object Flags {
// TODO(b/275694445): Tracking Bug
@JvmField
- val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = unreleasedFlag(208,
+ val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = releasedFlag(208,
"lockscreen_without_secure_lock_when_dreaming")
/**
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index d3b6fc237084..5189944e908b 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -97,6 +97,7 @@ import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
+import com.android.app.animation.Interpolators;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
@@ -116,7 +117,6 @@ import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.animation.DialogCuj;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.animation.Expandable;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Background;
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
index b86083abad21..1f1329111ce7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
@@ -106,7 +106,7 @@ constructor(
}
private fun isPhysicalFullKeyboard(deviceId: Int): Boolean {
- val device = inputManager.getInputDevice(deviceId)
+ val device = inputManager.getInputDevice(deviceId) ?: return false
return !device.isVirtual && device.isFullKeyboard
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 9ab2e9922531..9844ca02482b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -36,7 +36,7 @@ import androidx.core.math.MathUtils
import com.android.internal.R
import com.android.keyguard.KeyguardClockSwitchController
import com.android.keyguard.KeyguardViewController
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -791,8 +791,9 @@ class KeyguardUnlockAnimationController @Inject constructor(
// Translate up from the bottom.
surfaceBehindMatrix.setTranslate(
- surfaceBehindRemoteAnimationTarget.screenSpaceBounds.left.toFloat(),
- surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
+ surfaceBehindRemoteAnimationTarget.localBounds.left.toFloat(),
+ surfaceBehindRemoteAnimationTarget.localBounds.top.toFloat() +
+ surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
)
// Scale up from a point at the center-bottom of the surface.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 7a2013e2c612..99a9bed5a2bb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -98,6 +98,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.app.animation.Interpolators;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -121,7 +122,6 @@ import com.android.systemui.Dumpable;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
@@ -1610,8 +1610,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
private void doKeyguardLaterForChildProfilesLocked() {
- UserManager um = UserManager.get(mContext);
- for (int profileId : um.getEnabledProfileIds(UserHandle.myUserId())) {
+ for (UserInfo profile : mUserTracker.getUserProfiles()) {
+ if (!profile.isEnabled()) continue;
+ final int profileId = profile.id;
if (mLockPatternUtils.isSeparateProfileChallengeEnabled(profileId)) {
long userTimeout = getLockTimeout(profileId);
if (userTimeout == 0) {
@@ -1634,8 +1635,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
}
private void doKeyguardForChildProfilesLocked() {
- UserManager um = UserManager.get(mContext);
- for (int profileId : um.getEnabledProfileIds(UserHandle.myUserId())) {
+ for (UserInfo profile : mUserTracker.getUserProfiles()) {
+ if (!profile.isEnabled()) continue;
+ final int profileId = profile.id;
if (mLockPatternUtils.isSeparateProfileChallengeEnabled(profileId)) {
lockProfile(profileId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index e6568f20bc20..cde67f979132 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
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 c2d139c21074..7e9cbc1a9772 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
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 86f65dde4031..aca40195dbcf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
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 3beac0b1322e..fc7bfb4e45f4 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
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
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 b5bcd45f03dd..39c630b1fa6f 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
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
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 87f3164b33d2..0505d37262b0 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
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index 1fbfff95ab7e..944adbab3906 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -17,7 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 94961cbf4240..d4af38185f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -17,10 +17,10 @@
package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
+import com.android.app.animation.Interpolators
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index 38b9d508f81c..9d7477c13be6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -16,7 +16,7 @@
package com.android.systemui.keyguard.ui
import android.view.animation.Interpolator
-import com.android.systemui.animation.Interpolators.LINEAR
+import com.android.app.animation.Interpolators.LINEAR
import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
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 d96609c24dbd..c8d37a165a0e 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
@@ -32,11 +32,11 @@ import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.animation.Interpolators
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.Expandable
-import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.binder.IconViewBinder
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index 8d6545a49a8a..2c9a9b3271e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -16,8 +16,8 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
-import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE
+import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
index f16827d4a54a..c1357863f3a5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
@@ -16,7 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index bc9dc4f69102..c6187dde035b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -16,7 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index a60665a81f0e..d3ea89ce1935 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -16,7 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index ddce516a0fb2..6845c55b8385 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -16,7 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE
+import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index df93d235245c..68810f909016 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -16,7 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt
index 37d956bd09eb..e38abc2228c4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt
@@ -21,9 +21,9 @@ import android.animation.ObjectAnimator
import android.text.format.DateUtils
import androidx.annotation.UiThread
import androidx.lifecycle.Observer
+import com.android.app.animation.Interpolators
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
import com.android.systemui.media.controls.ui.SquigglyProgress
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt
index 3669493f4e41..b46ebb22ff05 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt
@@ -34,10 +34,10 @@ import android.util.AttributeSet
import android.util.MathUtils
import android.view.View
import androidx.annotation.Keep
+import com.android.app.animation.Interpolators
import com.android.internal.graphics.ColorUtils
import com.android.internal.graphics.ColorUtils.blendARGB
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
import org.xmlpull.v1.XmlPullParser
private const val BACKGROUND_ANIM_DURATION = 370L
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt
index dd5c2bf497cb..937a618df68f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt
@@ -35,9 +35,9 @@ import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.util.MathUtils.lerp
import androidx.annotation.Keep
+import com.android.app.animation.Interpolators
import com.android.internal.graphics.ColorUtils
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
import org.xmlpull.v1.XmlPullParser
private const val RIPPLE_ANIM_DURATION = 800L
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index ab394428ad8d..0aa434976ce7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -166,6 +166,7 @@ constructor(
}
}
+ /** Whether the media card currently has the "expanded" layout */
@VisibleForTesting
var currentlyExpanded = true
set(value) {
@@ -501,6 +502,7 @@ constructor(
mediaHostStatesManager.addCallback(
object : MediaHostStatesManager.Callback {
override fun onHostStateChanged(location: Int, mediaHostState: MediaHostState) {
+ updateUserVisibility()
if (location == desiredLocation) {
onDesiredLocationChanged(desiredLocation, mediaHostState, animate = false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 40027a1d8f2c..f9d3094d6f00 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -72,6 +72,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.constraintlayout.widget.ConstraintSet;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.internal.jank.InteractionJankMonitor;
@@ -82,7 +83,6 @@ import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.GhostedViewLaunchAnimatorController;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.bluetooth.BroadcastDialogController;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.qualifiers.Background;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
index 54237ce7cf25..fe8ebafdf9b4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
@@ -33,9 +33,9 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroupOverlay
import androidx.annotation.VisibleForTesting
+import com.android.app.animation.Interpolators
import com.android.keyguard.KeyguardViewController
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dreams.DreamOverlayStateController
@@ -257,7 +257,7 @@ constructor(
if (value && (isLockScreenShadeVisibleToUser() || isHomeScreenShadeVisibleToUser())) {
mediaCarouselController.logSmartspaceImpression(value)
}
- mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
+ updateUserVisibility()
}
/**
@@ -460,8 +460,7 @@ constructor(
) {
mediaCarouselController.logSmartspaceImpression(qsExpanded)
}
- mediaCarouselController.mediaCarouselScrollHandler.visibleToUser =
- isVisibleToUser()
+ updateUserVisibility()
}
override fun onDozeAmountChanged(linear: Float, eased: Float) {
@@ -480,8 +479,7 @@ constructor(
qsExpanded = false
closeGuts()
}
- mediaCarouselController.mediaCarouselScrollHandler.visibleToUser =
- isVisibleToUser()
+ updateUserVisibility()
}
override fun onExpandedChanged(isExpanded: Boolean) {
@@ -489,8 +487,7 @@ constructor(
if (isHomeScreenShadeVisibleToUser()) {
mediaCarouselController.logSmartspaceImpression(qsExpanded)
}
- mediaCarouselController.mediaCarouselScrollHandler.visibleToUser =
- isVisibleToUser()
+ updateUserVisibility()
}
}
)
@@ -532,9 +529,7 @@ constructor(
}
)
- mediaCarouselController.updateUserVisibility = {
- mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
- }
+ mediaCarouselController.updateUserVisibility = this::updateUserVisibility
mediaCarouselController.updateHostVisibility = {
mediaHosts.forEach { it?.updateViewVisibility() }
}
@@ -1180,11 +1175,15 @@ constructor(
return isCrossFadeAnimatorRunning
}
- /** Returns true when the media card could be visible to the user if existed. */
- private fun isVisibleToUser(): Boolean {
- return isLockScreenVisibleToUser() ||
- isLockScreenShadeVisibleToUser() ||
- isHomeScreenShadeVisibleToUser()
+ /** Update whether or not the media carousel could be visible to the user */
+ private fun updateUserVisibility() {
+ val shadeVisible =
+ isLockScreenVisibleToUser() ||
+ isLockScreenShadeVisibleToUser() ||
+ isHomeScreenShadeVisibleToUser()
+ val mediaVisible = qsExpanded || hasActiveMediaOrRecommendation
+ mediaCarouselController.mediaCarouselScrollHandler.visibleToUser =
+ shadeVisible && mediaVisible
}
private fun isLockScreenVisibleToUser(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt
index e9b2cf2b18d1..583c626d2156 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt
@@ -31,8 +31,8 @@ import android.util.MathUtils.lerp
import android.util.MathUtils.lerpInv
import android.util.MathUtils.lerpInvSat
import androidx.annotation.VisibleForTesting
+import com.android.app.animation.Interpolators
import com.android.internal.graphics.ColorUtils
-import com.android.systemui.animation.Interpolators
import kotlin.math.abs
import kotlin.math.cos
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 78082c3eb3c6..77ff0362851a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -36,7 +36,7 @@ import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE
import com.android.internal.widget.CachingIconView
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.ui.binder.TintedIconViewBinder
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskLabelLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskLabelLoader.kt
index eadcb93a7f94..1be8b70c1d42 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskLabelLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskLabelLoader.kt
@@ -20,6 +20,7 @@ import android.annotation.UserIdInt
import android.content.ComponentName
import android.content.pm.PackageManager
import android.os.UserHandle
+import android.util.Log
import com.android.systemui.dagger.qualifiers.Background
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -36,18 +37,27 @@ constructor(
private val packageManager: PackageManager
) : RecentTaskLabelLoader {
+ private val TAG = "RecentTaskLabelLoader"
+
override suspend fun loadLabel(
@UserIdInt userId: Int,
componentName: ComponentName
): CharSequence? =
withContext(coroutineDispatcher) {
- val userHandle = UserHandle(userId)
- val appInfo =
- packageManager.getApplicationInfo(
- componentName.packageName,
- PackageManager.ApplicationInfoFlags.of(0 /* no flags */)
- )
- val label = packageManager.getApplicationLabel(appInfo)
- return@withContext packageManager.getUserBadgedLabel(label, userHandle)
+ var badgedLabel: CharSequence? = null
+ try {
+ val appInfo =
+ packageManager.getApplicationInfoAsUser(
+ componentName.packageName,
+ PackageManager.ApplicationInfoFlags.of(0 /* no flags */),
+ userId
+ )
+ val label = packageManager.getApplicationLabel(appInfo)
+ val userHandle = UserHandle(userId)
+ badgedLabel = packageManager.getUserBadgedLabel(label, userHandle)
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e(TAG, "Unable to get application info", e)
+ }
+ return@withContext badgedLabel
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
index 64f97f2faacc..2d75359bf835 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
@@ -68,7 +68,7 @@ constructor(
}
launch {
val label = labelLoader.loadLabel(task.userId, component)
- root.contentDescription = label
+ root.contentDescription = label ?: root.context.getString(R.string.unknown)
}
}
launch {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 94f01b87f936..146b5f57630e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -58,11 +58,11 @@ import android.widget.FrameLayout;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.Utils;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
import com.android.systemui.navigationbar.buttons.ContextualButton;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java
index 02180160fc11..10084bd4ccad 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java
@@ -16,7 +16,7 @@
package com.android.systemui.navigationbar.buttons;
-import static com.android.systemui.animation.Interpolators.LINEAR;
+import static com.android.app.animation.Interpolators.LINEAR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -24,9 +24,6 @@ import android.animation.ValueAnimator;
import android.view.View;
import android.view.View.AccessibilityDelegate;
-import com.android.systemui.Dependency;
-import com.android.systemui.assist.AssistManager;
-
import java.util.ArrayList;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 3770b2885b18..96e97565d585 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -60,6 +60,7 @@ private const val MIN_DURATION_CANCELLED_ANIMATION = 200L
private const val MIN_DURATION_COMMITTED_ANIMATION = 80L
private const val MIN_DURATION_COMMITTED_AFTER_FLING_ANIMATION = 120L
private const val MIN_DURATION_INACTIVE_BEFORE_FLUNG_ANIMATION = 50L
+private const val MIN_DURATION_INACTIVE_BEFORE_ACTIVE_ANIMATION = 80L
private const val MIN_DURATION_FLING_ANIMATION = 160L
private const val MIN_DURATION_ENTRY_TO_ACTIVE_CONSIDERED_AS_FLING = 100L
@@ -135,7 +136,8 @@ class BackPanelController internal constructor(
private lateinit var backCallback: NavigationEdgeBackPlugin.BackCallback
private var previousXTranslationOnActiveOffset = 0f
private var previousXTranslation = 0f
- private var totalTouchDelta = 0f
+ private var totalTouchDeltaActive = 0f
+ private var totalTouchDeltaInactive = 0f
private var touchDeltaStartX = 0f
private var velocityTracker: VelocityTracker? = null
set(value) {
@@ -154,7 +156,7 @@ class BackPanelController internal constructor(
private var gestureEntryTime = 0L
private var gestureInactiveTime = 0L
- private var gestureActiveTime = 0L
+ private var gesturePastActiveThresholdWhileInactiveTime = 0L
private val elapsedTimeSinceInactive
get() = SystemClock.uptimeMillis() - gestureInactiveTime
@@ -250,7 +252,7 @@ class BackPanelController internal constructor(
private fun updateConfiguration() {
params.update(resources)
mView.updateArrowPaint(params.arrowThickness)
- minFlingDistance = ViewConfiguration.get(context).scaledTouchSlop * 3
+ minFlingDistance = viewConfiguration.scaledTouchSlop * 3
}
private val configurationListener = object : ConfigurationController.ConfigurationListener {
@@ -403,30 +405,46 @@ class BackPanelController internal constructor(
}
GestureState.ACTIVE -> {
val isPastDynamicDeactivationThreshold =
- totalTouchDelta <= params.deactivationSwipeTriggerThreshold
+ totalTouchDeltaActive <= params.deactivationTriggerThreshold
val isMinDurationElapsed =
- elapsedTimeSinceEntry > MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION
+ elapsedTimeSinceEntry > MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION
if (isMinDurationElapsed && (!isWithinYActivationThreshold ||
- isPastDynamicDeactivationThreshold)
+ isPastDynamicDeactivationThreshold)
) {
updateArrowState(GestureState.INACTIVE)
}
}
GestureState.INACTIVE -> {
val isPastStaticThreshold =
- xTranslation > params.staticTriggerThreshold
- val isPastDynamicReactivationThreshold = totalTouchDelta > 0 &&
- abs(totalTouchDelta) >=
- params.reactivationTriggerThreshold
-
- if (isPastStaticThreshold &&
+ xTranslation > params.staticTriggerThreshold
+ val isPastDynamicReactivationThreshold =
+ totalTouchDeltaInactive >= params.reactivationTriggerThreshold
+ val isPastAllThresholds = isPastStaticThreshold &&
isPastDynamicReactivationThreshold &&
isWithinYActivationThreshold
- ) {
+ val isPastAllThresholdsForFirstTime = isPastAllThresholds &&
+ gesturePastActiveThresholdWhileInactiveTime == 0L
+
+ gesturePastActiveThresholdWhileInactiveTime = when {
+ isPastAllThresholdsForFirstTime -> SystemClock.uptimeMillis()
+ isPastAllThresholds -> gesturePastActiveThresholdWhileInactiveTime
+ else -> 0L
+ }
+
+ val elapsedTimePastAllThresholds =
+ SystemClock.uptimeMillis() - gesturePastActiveThresholdWhileInactiveTime
+
+ val isPastMinimumInactiveToActiveDuration =
+ elapsedTimePastAllThresholds > MIN_DURATION_INACTIVE_BEFORE_ACTIVE_ANIMATION
+
+ if (isPastAllThresholds && isPastMinimumInactiveToActiveDuration) {
+ // The minimum duration adds the 'edge stickiness'
+ // factor before pulling it off edge
updateArrowState(GestureState.ACTIVE)
}
}
+
else -> {}
}
}
@@ -451,19 +469,25 @@ class BackPanelController internal constructor(
previousXTranslation = xTranslation
if (abs(xDelta) > 0) {
- val range =
- params.run { deactivationSwipeTriggerThreshold..reactivationTriggerThreshold }
- val isTouchInContinuousDirection =
- sign(xDelta) == sign(totalTouchDelta) || totalTouchDelta in range
+ val isInSameDirection = sign(xDelta) == sign(totalTouchDeltaActive)
+ val isInDynamicRange = totalTouchDeltaActive in params.dynamicTriggerThresholdRange
+ val isTouchInContinuousDirection = isInSameDirection || isInDynamicRange
if (isTouchInContinuousDirection) {
// Direction has NOT changed, so keep counting the delta
- totalTouchDelta += xDelta
+ totalTouchDeltaActive += xDelta
} else {
// Direction has changed, so reset the delta
- totalTouchDelta = xDelta
+ totalTouchDeltaActive = xDelta
touchDeltaStartX = x
}
+
+ // Add a slop to to prevent small jitters when arrow is at edge in
+ // emitting small values that cause the arrow to poke out slightly
+ val minimumDelta = -viewConfiguration.scaledTouchSlop.toFloat()
+ totalTouchDeltaInactive = totalTouchDeltaInactive
+ .plus(xDelta)
+ .coerceAtLeast(minimumDelta)
}
updateArrowStateOnMove(yTranslation, xTranslation)
@@ -471,7 +495,7 @@ class BackPanelController internal constructor(
val gestureProgress = when (currentState) {
GestureState.ACTIVE -> fullScreenProgress(xTranslation)
GestureState.ENTRY -> staticThresholdProgress(xTranslation)
- GestureState.INACTIVE -> reactivationThresholdProgress(totalTouchDelta)
+ GestureState.INACTIVE -> reactivationThresholdProgress(totalTouchDeltaInactive)
else -> null
}
@@ -529,8 +553,7 @@ class BackPanelController internal constructor(
* the arrow is fully stretched (between 0.0 - 1.0f)
*/
private fun fullScreenProgress(xTranslation: Float): Float {
- val progress = abs((xTranslation - previousXTranslationOnActiveOffset) /
- (fullyStretchedThreshold - previousXTranslationOnActiveOffset))
+ val progress = (xTranslation - previousXTranslationOnActiveOffset) / fullyStretchedThreshold
return MathUtils.saturate(progress)
}
@@ -581,13 +604,15 @@ class BackPanelController internal constructor(
}
private var previousPreThresholdWidthInterpolator = params.entryWidthTowardsEdgeInterpolator
- fun preThresholdWidthStretchAmount(progress: Float): Float {
+ private fun preThresholdWidthStretchAmount(progress: Float): Float {
val interpolator = run {
- val isPastSlop = abs(totalTouchDelta) > ViewConfiguration.get(context).scaledTouchSlop
+ val isPastSlop = totalTouchDeltaInactive > viewConfiguration.scaledTouchSlop
if (isPastSlop) {
- if (totalTouchDelta > 0) {
+ if (totalTouchDeltaInactive > 0) {
params.entryWidthInterpolator
- } else params.entryWidthTowardsEdgeInterpolator
+ } else {
+ params.entryWidthTowardsEdgeInterpolator
+ }
} else {
previousPreThresholdWidthInterpolator
}.also { previousPreThresholdWidthInterpolator = it }
@@ -643,7 +668,7 @@ class BackPanelController internal constructor(
xVelocity.takeIf { mView.isLeftPanel } ?: (xVelocity * -1)
} ?: 0f
val isPastFlingVelocityThreshold =
- flingVelocity > ViewConfiguration.get(context).scaledMinimumFlingVelocity
+ flingVelocity > viewConfiguration.scaledMinimumFlingVelocity
return flingDistance > minFlingDistance && isPastFlingVelocityThreshold
}
@@ -861,7 +886,6 @@ class BackPanelController internal constructor(
}
GestureState.ACTIVE -> {
previousXTranslationOnActiveOffset = previousXTranslation
- gestureActiveTime = SystemClock.uptimeMillis()
updateRestingArrowDimens()
@@ -870,21 +894,24 @@ class BackPanelController internal constructor(
vibratorHelper.vibrate(VIBRATE_ACTIVATED_EFFECT)
}
- val startingVelocity = convertVelocityToSpringStartingVelocity(
- valueOnFastVelocity = 0f,
- valueOnSlowVelocity = if (previousState == GestureState.ENTRY) 2f else 4.5f
- )
+ val minimumPop = 2f
+ val maximumPop = 4.5f
when (previousState) {
- GestureState.ENTRY,
- GestureState.INACTIVE -> {
+ GestureState.ENTRY -> {
+ val startingVelocity = convertVelocityToSpringStartingVelocity(
+ valueOnFastVelocity = minimumPop,
+ valueOnSlowVelocity = maximumPop,
+ fastVelocityBound = 1f,
+ slowVelocityBound = 0.5f,
+ )
mView.popOffEdge(startingVelocity)
}
- GestureState.COMMITTED -> {
- // if previous state was committed then this activation
- // was due to a quick second swipe. Don't pop the arrow this time
+ GestureState.INACTIVE -> {
+ mView.popOffEdge(maximumPop)
}
- else -> { }
+
+ else -> {}
}
}
@@ -896,7 +923,7 @@ class BackPanelController internal constructor(
// but because we can also independently enter this state
// if touch Y >> touch X, we force it to deactivationSwipeTriggerThreshold
// so that gesture progress in this state is consistent regardless of entry
- totalTouchDelta = params.deactivationSwipeTriggerThreshold
+ totalTouchDeltaInactive = params.deactivationTriggerThreshold
val startingVelocity = convertVelocityToSpringStartingVelocity(
valueOnFastVelocity = -1.05f,
@@ -944,10 +971,12 @@ class BackPanelController internal constructor(
private fun convertVelocityToSpringStartingVelocity(
valueOnFastVelocity: Float,
valueOnSlowVelocity: Float,
+ fastVelocityBound: Float = 3f,
+ slowVelocityBound: Float = 0f,
): Float {
val factor = velocityTracker?.run {
computeCurrentVelocity(PX_PER_MS)
- MathUtils.smoothStep(0f, 3f, abs(xVelocity))
+ MathUtils.smoothStep(slowVelocityBound, fastVelocityBound, abs(xVelocity))
} ?: valueOnFastVelocity
return MathUtils.lerp(valueOnFastVelocity, valueOnSlowVelocity, 1 - factor)
@@ -982,7 +1011,7 @@ class BackPanelController internal constructor(
"$currentState",
"startX=$startX",
"startY=$startY",
- "xDelta=${"%.1f".format(totalTouchDelta)}",
+ "xDelta=${"%.1f".format(totalTouchDeltaActive)}",
"xTranslation=${"%.1f".format(previousXTranslation)}",
"pre=${"%.0f".format(staticThresholdProgress(previousXTranslation) * 100)}%",
"post=${"%.0f".format(fullScreenProgress(previousXTranslation) * 100)}%"
@@ -1023,7 +1052,7 @@ class BackPanelController internal constructor(
}
drawVerticalLine(x = params.staticTriggerThreshold, color = Color.BLUE)
- drawVerticalLine(x = params.deactivationSwipeTriggerThreshold, color = Color.BLUE)
+ drawVerticalLine(x = params.deactivationTriggerThreshold, color = Color.BLUE)
drawVerticalLine(x = startX, color = Color.GREEN)
drawVerticalLine(x = previousXTranslation, color = Color.DKGRAY)
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index b9ef916eebdf..41e3e6d83eea 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -89,6 +89,7 @@ import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
import com.android.systemui.util.Assert;
import com.android.wm.shell.back.BackAnimation;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.pip.Pip;
import java.io.PrintWriter;
@@ -190,6 +191,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
private final WindowManager mWindowManager;
private final IWindowManager mWindowManagerService;
private final Optional<Pip> mPipOptional;
+ private final Optional<DesktopMode> mDesktopModeOptional;
private final FalsingManager mFalsingManager;
private final Configuration mLastReportedConfig = new Configuration();
// Activities which should not trigger Back gesture.
@@ -204,6 +206,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
private final Rect mPipExcludedBounds = new Rect();
private final Rect mNavBarOverlayExcludedBounds = new Rect();
private final Region mExcludeRegion = new Region();
+ private final Region mDesktopModeExcludeRegion = new Region();
private final Region mUnrestrictedExcludeRegion = new Region();
private final Provider<NavigationBarEdgePanel> mNavBarEdgePanelProvider;
private final Provider<BackGestureTfClassifierProvider>
@@ -328,6 +331,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
private final Consumer<Boolean> mOnIsInPipStateChangedListener =
(isInPip) -> mIsInPip = isInPip;
+ private final Consumer<Region> mDesktopCornersChangedListener =
+ (desktopExcludeRegion) -> mDesktopModeExcludeRegion.set(desktopExcludeRegion);
+
private final UserTracker.Callback mUserChangedCallback =
new UserTracker.Callback() {
@Override
@@ -352,6 +358,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
WindowManager windowManager,
IWindowManager windowManagerService,
Optional<Pip> pipOptional,
+ Optional<DesktopMode> desktopModeOptional,
FalsingManager falsingManager,
Provider<NavigationBarEdgePanel> navigationBarEdgePanelProvider,
Provider<BackGestureTfClassifierProvider> backGestureTfClassifierProviderProvider,
@@ -372,6 +379,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mWindowManager = windowManager;
mWindowManagerService = windowManagerService;
mPipOptional = pipOptional;
+ mDesktopModeOptional = desktopModeOptional;
mFalsingManager = falsingManager;
mNavBarEdgePanelProvider = navigationBarEdgePanelProvider;
mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider;
@@ -580,6 +588,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mMainExecutor::execute, mOnPropertiesChangedListener);
mPipOptional.ifPresent(
pip -> pip.setOnIsInPipStateChangedListener(mOnIsInPipStateChangedListener));
+ mDesktopModeOptional.ifPresent(
+ dm -> dm.addDesktopGestureExclusionRegionListener(
+ mDesktopCornersChangedListener, mMainExecutor));
try {
mWindowManagerService.registerSystemGestureExclusionListener(
@@ -802,11 +813,17 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mDisplaySize.y - insets.bottom);
}
+ private boolean desktopExcludeRegionContains(int x, int y) {
+ return mDesktopModeExcludeRegion.contains(x, y);
+ }
+
private boolean isWithinTouchRegion(int x, int y) {
// If the point is inside the PiP or Nav bar overlay excluded bounds, then ignore the back
// gesture
final boolean isInsidePip = mIsInPip && mPipExcludedBounds.contains(x, y);
- if (isInsidePip || mNavBarOverlayExcludedBounds.contains(x, y)) {
+ final boolean isInDesktopExcludeRegion = desktopExcludeRegionContains(x, y);
+ if (isInsidePip || isInDesktopExcludeRegion
+ || mNavBarOverlayExcludedBounds.contains(x, y)) {
return false;
}
@@ -1136,6 +1153,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
pw.println(" mIsInPip=" + mIsInPip);
pw.println(" mPipExcludedBounds=" + mPipExcludedBounds);
+ pw.println(" mDesktopModeExclusionRegion=" + mDesktopModeExcludeRegion);
pw.println(" mNavBarOverlayExcludedBounds=" + mNavBarOverlayExcludedBounds);
pw.println(" mEdgeWidthLeft=" + mEdgeWidthLeft);
pw.println(" mEdgeWidthRight=" + mEdgeWidthRight);
@@ -1206,6 +1224,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
private final WindowManager mWindowManager;
private final IWindowManager mWindowManagerService;
private final Optional<Pip> mPipOptional;
+ private final Optional<DesktopMode> mDesktopModeOptional;
private final FalsingManager mFalsingManager;
private final Provider<NavigationBarEdgePanel> mNavBarEdgePanelProvider;
private final Provider<BackGestureTfClassifierProvider>
@@ -1227,6 +1246,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
WindowManager windowManager,
IWindowManager windowManagerService,
Optional<Pip> pipOptional,
+ Optional<DesktopMode> desktopModeOptional,
FalsingManager falsingManager,
Provider<NavigationBarEdgePanel> navBarEdgePanelProvider,
Provider<BackGestureTfClassifierProvider>
@@ -1246,6 +1266,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mWindowManager = windowManager;
mWindowManagerService = windowManagerService;
mPipOptional = pipOptional;
+ mDesktopModeOptional = desktopModeOptional;
mFalsingManager = falsingManager;
mNavBarEdgePanelProvider = navBarEdgePanelProvider;
mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider;
@@ -1270,6 +1291,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
mWindowManager,
mWindowManagerService,
mPipOptional,
+ mDesktopModeOptional,
mFalsingManager,
mNavBarEdgePanelProvider,
mBackGestureTfClassifierProviderProvider,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
index c9d8c8495dcc..876c74a9f3e3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -72,9 +72,11 @@ data class EdgePanelParams(private var resources: Resources) {
private set
var reactivationTriggerThreshold: Float = 0f
private set
- var deactivationSwipeTriggerThreshold: Float = 0f
+ var deactivationTriggerThreshold: Float = 0f
get() = -field
private set
+ lateinit var dynamicTriggerThresholdRange: ClosedRange<Float>
+ private set
var swipeProgressThreshold: Float = 0f
private set
@@ -122,8 +124,10 @@ data class EdgePanelParams(private var resources: Resources) {
staticTriggerThreshold = getDimen(R.dimen.navigation_edge_action_drag_threshold)
reactivationTriggerThreshold =
getDimen(R.dimen.navigation_edge_action_reactivation_drag_threshold)
- deactivationSwipeTriggerThreshold =
+ deactivationTriggerThreshold =
getDimen(R.dimen.navigation_edge_action_deactivation_drag_threshold)
+ dynamicTriggerThresholdRange =
+ reactivationTriggerThreshold..deactivationTriggerThreshold
swipeProgressThreshold = getDimen(R.dimen.navigation_edge_action_progress_threshold)
entryWidthInterpolator = PathInterpolator(.19f, 1.27f, .71f, .86f)
@@ -136,7 +140,6 @@ data class EdgePanelParams(private var resources: Resources) {
edgeCornerInterpolator = PathInterpolator(0f, 1.11f, .85f, .84f)
heightInterpolator = PathInterpolator(1f, .05f, .9f, -0.29f)
- val entryActiveHorizontalTranslationSpring = createSpring(800f, 0.76f)
val activeCommittedArrowLengthSpring = createSpring(1500f, 0.29f)
val activeCommittedArrowHeightSpring = createSpring(1500f, 0.29f)
val flungCommittedEdgeCornerSpring = createSpring(10000f, 1f)
@@ -150,7 +153,7 @@ data class EdgePanelParams(private var resources: Resources) {
horizontalTranslation = getDimen(R.dimen.navigation_edge_entry_margin),
scale = getDimenFloat(R.dimen.navigation_edge_entry_scale),
scalePivotX = getDimen(R.dimen.navigation_edge_pre_threshold_background_width),
- horizontalTranslationSpring = entryActiveHorizontalTranslationSpring,
+ horizontalTranslationSpring = createSpring(500f, 0.76f),
verticalTranslationSpring = createSpring(30000f, 1f),
scaleSpring = createSpring(120f, 0.8f),
arrowDimens = ArrowDimens(
@@ -202,7 +205,7 @@ data class EdgePanelParams(private var resources: Resources) {
activeIndicator = BackIndicatorDimens(
horizontalTranslation = getDimen(R.dimen.navigation_edge_active_margin),
scale = getDimenFloat(R.dimen.navigation_edge_active_scale),
- horizontalTranslationSpring = entryActiveHorizontalTranslationSpring,
+ horizontalTranslationSpring = createSpring(1000f, 0.7f),
scaleSpring = createSpring(450f, 0.39f),
scalePivotX = getDimen(R.dimen.navigation_edge_active_background_width),
arrowDimens = ArrowDimens(
@@ -222,8 +225,8 @@ data class EdgePanelParams(private var resources: Resources) {
farCornerRadius = getDimen(R.dimen.navigation_edge_active_far_corners),
widthSpring = createSpring(850f, 0.75f),
heightSpring = createSpring(10000f, 1f),
- edgeCornerRadiusSpring = createSpring(600f, 0.36f),
- farCornerRadiusSpring = createSpring(2500f, 0.855f),
+ edgeCornerRadiusSpring = createSpring(2600f, 0.855f),
+ farCornerRadiusSpring = createSpring(1200f, 0.30f),
)
)
@@ -250,10 +253,10 @@ data class EdgePanelParams(private var resources: Resources) {
getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners),
farCornerRadius =
getDimen(R.dimen.navigation_edge_pre_threshold_far_corners),
- widthSpring = createSpring(250f, 0.65f),
+ widthSpring = createSpring(400f, 0.65f),
heightSpring = createSpring(1500f, 0.45f),
- farCornerRadiusSpring = createSpring(200f, 1f),
- edgeCornerRadiusSpring = createSpring(150f, 0.5f),
+ farCornerRadiusSpring = createSpring(300f, 1f),
+ edgeCornerRadiusSpring = createSpring(250f, 0.5f),
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index 590efbb66454..ff22398cc3be 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -48,10 +48,10 @@ import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
+import com.android.app.animation.Interpolators;
import com.android.internal.util.LatencyTracker;
import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
import com.android.systemui.settings.DisplayTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index aab898e2efcf..8aec0c61c75c 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -284,7 +284,15 @@ constructor(
/** @see OnRoleHoldersChangedListener */
fun onRoleHoldersChanged(roleName: String, user: UserHandle) {
- if (roleName == ROLE_NOTES) updateNoteTaskAsUser(user)
+ if (roleName != ROLE_NOTES) return
+
+ if (user == userTracker.userHandle) {
+ updateNoteTaskAsUser(user)
+ } else {
+ // TODO(b/278729185): Replace fire and forget service with a bounded service.
+ val intent = NoteTaskControllerUpdateService.createIntent(context)
+ context.startServiceAsUser(intent, user)
+ }
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt
new file mode 100644
index 000000000000..26b35cc8ffd1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.notetask
+
+import android.content.Context
+import android.content.Intent
+import androidx.lifecycle.LifecycleService
+import javax.inject.Inject
+
+/**
+ * A fire & forget service for updating note task shortcuts.
+ *
+ * The main use is to update shortcuts in different user by launching it using `startServiceAsUser`.
+ * The service will open with access to a context from that user, trigger
+ * [NoteTaskController.updateNoteTaskAsUser] and [stopSelf] immediately.
+ *
+ * The fire and forget approach was created due to its simplicity but may use unnecessary resources
+ * by recreating the services. We will investigate its impacts and consider to move to a bounded
+ * services - the implementation is more complex as a bounded service is asynchronous by default.
+ *
+ * TODO(b/278729185): Replace fire and forget service with a bounded service.
+ */
+@InternalNoteTaskApi
+class NoteTaskControllerUpdateService
+@Inject
+constructor(
+ val controller: NoteTaskController,
+) : LifecycleService() {
+
+ override fun onCreate() {
+ super.onCreate()
+ // TODO(b/278729185): Replace fire and forget service with a bounded service.
+ controller.updateNoteTaskAsUser(user)
+ stopSelf()
+ }
+
+ companion object {
+ fun createIntent(context: Context): Intent =
+ Intent(context, NoteTaskControllerUpdateService::class.java)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index 1839dfd3fe32..a166393ec29c 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -14,9 +14,12 @@
* limitations under the License.
*/
+@file:OptIn(InternalNoteTaskApi::class)
+
package com.android.systemui.notetask
import android.app.Activity
+import android.app.Service
import android.app.role.RoleManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -34,6 +37,9 @@ import dagger.multibindings.IntoMap
@Module(includes = [NoteTaskQuickAffordanceModule::class])
interface NoteTaskModule {
+ @[Binds IntoMap ClassKey(NoteTaskControllerUpdateService::class)]
+ fun NoteTaskControllerUpdateService.bindNoteTaskControllerUpdateService(): Service
+
@[Binds IntoMap ClassKey(LaunchNoteTaskActivity::class)]
fun LaunchNoteTaskActivity.bindNoteTaskLauncherActivity(): Activity
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 0641eec154bb..a3b901b675ed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -44,6 +44,7 @@ import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.GuardedBy
import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@@ -201,7 +202,7 @@ class FgsManagerControllerImpl @Inject constructor(
@GuardedBy("lock")
private val appListAdapter: AppListAdapter = AppListAdapter()
- @GuardedBy("lock")
+ /* Only mutate on the background thread */
private var runningApps: ArrayMap<UserPackage, RunningApp> = ArrayMap()
private val userTrackerCallback = object : UserTracker.Callback {
@@ -374,11 +375,6 @@ class FgsManagerControllerImpl @Inject constructor(
override fun showDialog(expandable: Expandable?) {
synchronized(lock) {
if (dialog == null) {
-
- runningTaskIdentifiers.keys.forEach {
- it.updateUiControl()
- }
-
val dialog = SystemUIDialog(context)
dialog.setTitle(R.string.fgs_manager_dialog_title)
dialog.setMessage(R.string.fgs_manager_dialog_message)
@@ -421,33 +417,53 @@ class FgsManagerControllerImpl @Inject constructor(
}
}
- backgroundExecutor.execute {
- synchronized(lock) {
- updateAppItemsLocked()
- }
- }
+ updateAppItemsLocked(refreshUiControls = true)
}
}
}
@GuardedBy("lock")
- private fun updateAppItemsLocked() {
+ private fun updateAppItemsLocked(refreshUiControls: Boolean = false) {
if (dialog == null) {
- runningApps.clear()
+ backgroundExecutor.execute {
+ clearRunningApps()
+ }
return
}
- val addedPackages = runningTaskIdentifiers.keys.filter {
- currentProfileIds.contains(it.userId) &&
+ val packagesToStartTime = runningTaskIdentifiers.mapValues { it.value.startTime }
+ val profileIds = currentProfileIds.toSet()
+ backgroundExecutor.execute {
+ updateAppItems(packagesToStartTime, profileIds, refreshUiControls)
+ }
+ }
+
+ /**
+ * Must be called on the background thread.
+ */
+ @WorkerThread
+ private fun updateAppItems(
+ packages: Map<UserPackage, Long>,
+ profileIds: Set<Int>,
+ refreshUiControls: Boolean = true
+ ) {
+ if (refreshUiControls) {
+ packages.forEach { (pkg, _) ->
+ pkg.updateUiControl()
+ }
+ }
+
+ val addedPackages = packages.keys.filter {
+ profileIds.contains(it.userId) &&
it.uiControl != UIControl.HIDE_ENTRY && runningApps[it]?.stopped != true
}
- val removedPackages = runningApps.keys.filter { !runningTaskIdentifiers.containsKey(it) }
+ val removedPackages = runningApps.keys.filter { it !in packages }
addedPackages.forEach {
val ai = packageManager.getApplicationInfoAsUser(it.packageName, 0, it.userId)
runningApps[it] = RunningApp(
it.userId, it.packageName,
- runningTaskIdentifiers[it]!!.startTime, it.uiControl,
+ packages[it]!!, it.uiControl,
packageManager.getApplicationLabel(ai),
packageManager.getUserBadgedIcon(
packageManager.getApplicationIcon(ai), UserHandle.of(it.userId)
@@ -472,6 +488,14 @@ class FgsManagerControllerImpl @Inject constructor(
}
}
+ /**
+ * Must be called on the background thread.
+ */
+ @WorkerThread
+ private fun clearRunningApps() {
+ runningApps.clear()
+ }
+
private fun stopPackage(userId: Int, packageName: String, timeStarted: Long) {
logEvent(stopped = true, packageName, userId, timeStarted)
val userPackageKey = UserPackage(userId, packageName)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index a7aac5a4824d..463c79c6696a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -26,7 +26,7 @@ import android.view.View.OnLayoutChangeListener;
import androidx.annotation.Nullable;
-import com.android.systemui.animation.Interpolators;
+import com.android.app.animation.Interpolators;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSTile;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 584d27f84ceb..09cc2c52ff0a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -18,9 +18,10 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
-import static com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
+import static com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.res.Configuration;
@@ -43,10 +44,10 @@ import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
+import com.android.app.animation.Interpolators;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dump.DumpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 3711a2f39b7b..fbf134db15f1 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -103,7 +103,7 @@ open class UserTrackerImpl internal constructor(
@GuardedBy("callbacks")
private val callbacks: MutableList<DataItem> = ArrayList()
- fun initialize(startingUser: Int) {
+ open fun initialize(startingUser: Int) {
if (initialized) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
index b445000c467d..5850a846eed6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
@@ -47,19 +47,36 @@ constructor(
viewsIdToTranslate =
setOf(
ViewIdToTranslate(R.id.quick_settings_panel, START, filterShade),
- ViewIdToTranslate(R.id.notification_stack_scroller, END, filterShade),
- ViewIdToTranslate(R.id.statusIcons, END, filterShade),
- ViewIdToTranslate(R.id.privacy_container, END, filterShade),
- ViewIdToTranslate(R.id.batteryRemainingIcon, END, filterShade),
- ViewIdToTranslate(R.id.carrier_group, END, filterShade),
- ViewIdToTranslate(R.id.clock, START, filterShade),
- ViewIdToTranslate(R.id.date, START, filterShade)),
+ ViewIdToTranslate(R.id.notification_stack_scroller, END, filterShade)),
progressProvider = progressProvider)
}
+ private val translateAnimatorStatusBar by lazy {
+ UnfoldConstantTranslateAnimator(
+ viewsIdToTranslate =
+ setOf(
+ ViewIdToTranslate(R.id.statusIcons, END, filterShade),
+ ViewIdToTranslate(R.id.privacy_container, END, filterShade),
+ ViewIdToTranslate(R.id.batteryRemainingIcon, END, filterShade),
+ ViewIdToTranslate(R.id.carrier_group, END, filterShade),
+ ViewIdToTranslate(R.id.clock, START, filterShade),
+ ViewIdToTranslate(R.id.date, START, filterShade)
+ ),
+ progressProvider = progressProvider
+ )
+ }
+
fun setup(root: ViewGroup) {
val translationMax =
context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings).toFloat()
translateAnimator.init(root, translationMax)
+ val splitShadeStatusBarViewGroup: ViewGroup? =
+ root.findViewById(R.id.split_shade_status_bar)
+ if (splitShadeStatusBarViewGroup != null) {
+ translateAnimatorStatusBar.init(
+ splitShadeStatusBarViewGroup,
+ translationMax
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index d28ccff1bfae..1d7a2798b200 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -22,10 +22,10 @@ import static android.view.MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
+import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE;
+import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
-import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE;
-import static com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
import static com.android.systemui.classifier.Classifier.GENERIC;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
@@ -90,6 +90,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
@@ -113,7 +114,6 @@ import com.android.systemui.Dumpable;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.classifier.Classifier;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index ef14d1cb7f63..7a79e8514510 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -50,6 +50,7 @@ import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
@@ -59,7 +60,6 @@ import com.android.internal.policy.SystemBarUtils;
import com.android.keyguard.FaceAuthApiRequestReason;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index f0815e93dccd..4131e7dbc98c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -34,10 +34,10 @@ import android.view.WindowInsets
import android.widget.TextView
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.motion.widget.MotionLayout
+import com.android.app.animation.Interpolators
import com.android.settingslib.Utils
import com.android.systemui.Dumpable
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index b0b9ab240988..4c6673cca473 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -17,15 +17,28 @@
package com.android.systemui.shade
import android.view.LayoutInflater
+import com.android.systemui.CoreStartable
import com.android.systemui.R
+import com.android.systemui.biometrics.AuthRippleController
+import com.android.systemui.biometrics.AuthRippleView
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import dagger.Binds
import dagger.Module
import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
/** Module for classes related to the notification shade. */
@Module
abstract class ShadeModule {
+
+ @Binds
+ @IntoMap
+ @ClassKey(AuthRippleController::class)
+ abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable
+
companion object {
@Provides
@SysUISingleton
@@ -59,5 +72,22 @@ abstract class ShadeModule {
): NotificationPanelView {
return notificationShadeWindowView.findViewById(R.id.notification_panel)
}
+
+ @Provides
+ @SysUISingleton
+ fun providesLightRevealScrim(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): LightRevealScrim {
+ return notificationShadeWindowView.findViewById(R.id.light_reveal_scrim)
+ }
+
+ // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+ @Provides
+ @SysUISingleton
+ fun providesAuthRippleView(
+ notificationShadeWindowView: NotificationShadeWindowView,
+ ): AuthRippleView? {
+ return notificationShadeWindowView.findViewById(R.id.auth_ripple)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
index 26149321946d..ba9d13d57a74 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
@@ -75,7 +75,11 @@ interface SmartspaceViewComponent {
)
}
- override fun startPendingIntent(pi: PendingIntent, showOnLockscreen: Boolean) {
+ override fun startPendingIntent(
+ view: View,
+ pi: PendingIntent,
+ showOnLockscreen: Boolean
+ ) {
if (showOnLockscreen) {
pi.send()
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index 63179dac7b8c..c1ebf1236def 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -18,8 +18,8 @@ package com.android.systemui.statusbar;
import android.view.View;
+import com.android.app.animation.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 54b341f529b0..1a32d70a9745 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -38,8 +38,8 @@ import android.view.ViewAnimationUtils;
import android.view.animation.Interpolator;
import android.widget.ImageView;
+import com.android.app.animation.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.wm.shell.animation.FlingAnimationUtils;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 9421524c73a7..823bb355a307 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -16,7 +16,7 @@ import android.util.AttributeSet
import android.util.MathUtils.lerp
import android.view.View
import android.view.animation.PathInterpolator
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold
import com.android.systemui.util.getColorWithAlpha
import com.android.systemui.util.leak.RotationUtils
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index faf592e47eb4..22589686520b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -18,7 +18,7 @@ import com.android.systemui.Dumpable
import com.android.systemui.ExpandHelper
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.biometrics.UdfpsKeyguardViewController
import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.FalsingCollector
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 72ae16e607c8..fb88a96c38c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -44,9 +44,9 @@ import android.util.Log;
import android.view.View;
import android.widget.ImageView;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 8dc78426da7d..d37cbcc00360 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -33,7 +33,7 @@ import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.systemui.Dumpable
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 7eb63da38028..5c3bacc1fe1b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -35,10 +35,10 @@ import android.view.animation.PathInterpolator;
import androidx.annotation.NonNull;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 976924a2159d..f9d4f1a89ee2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -31,7 +31,7 @@ import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt
index 575f354c6620..f1e51e21a9e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt
@@ -4,7 +4,7 @@ import android.content.Context
import android.content.res.Configuration
import android.util.MathUtils
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.policy.ConfigurationController
import dagger.assisted.Assisted
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
index 572c0e0da262..3d574caf3192 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
@@ -8,7 +8,7 @@ import android.util.MathUtils
import android.view.animation.PathInterpolator
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 77550038c94a..91c08a062b54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -52,10 +52,10 @@ import android.view.animation.Interpolator;
import androidx.core.graphics.ColorUtils;
+import com.android.app.animation.Interpolators;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.notification.NotificationIconDozeHelper;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.util.drawable.DrawableSize;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 79d01b4a73fd..d6a14604ffb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -42,6 +42,7 @@ import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
@@ -49,7 +50,6 @@ import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
index 2fa27ee454e8..67ab0601cbf8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java
@@ -25,8 +25,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
+import com.android.app.animation.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.notification.TransformState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index bfc4e9c47db5..eddb6835318d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -26,7 +26,7 @@ import android.view.View
import android.widget.FrameLayout
import com.android.internal.annotations.GuardedBy
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 826e28955afc..950dbd9300b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -353,7 +353,11 @@ constructor(
}
}
- override fun startPendingIntent(pi: PendingIntent, showOnLockscreen: Boolean) {
+ override fun startPendingIntent(
+ view: View,
+ pi: PendingIntent,
+ showOnLockscreen: Boolean
+ ) {
if (showOnLockscreen) {
pi.send()
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
index 0446165be5fc..b09b9f42e6af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
@@ -21,8 +21,8 @@ import android.util.Pools;
import android.view.View;
import android.widget.ImageView;
+import com.android.app.animation.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.notification.row.HybridNotificationView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt
index c22dbf615190..785e65dd4026 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt
@@ -3,7 +3,7 @@ package com.android.systemui.statusbar.notification
import android.util.MathUtils
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.animation.LaunchAnimator
import kotlin.math.min
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
index c22cd1ba4bc8..5a14200677b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
@@ -23,13 +23,13 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+import com.android.app.animation.Interpolators;
import com.android.internal.widget.IMessagingLayout;
import com.android.internal.widget.MessagingGroup;
import com.android.internal.widget.MessagingImageMessage;
import com.android.internal.widget.MessagingLinearLayout;
import com.android.internal.widget.MessagingMessage;
import com.android.internal.widget.MessagingPropertyAnimator;
-import com.android.systemui.animation.Interpolators;
import java.util.ArrayList;
import java.util.HashMap;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
index 3fc7b132f38f..a0456985e8d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
@@ -24,8 +24,8 @@ import android.graphics.ColorMatrixColorFilter;
import android.view.View;
import android.widget.ImageView;
+import com.android.app.animation.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.function.Consumer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index fe0b28d16239..9ba219903272 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -21,8 +21,8 @@ import android.view.animation.Interpolator
import androidx.annotation.VisibleForTesting
import androidx.core.animation.ObjectAnimator
import com.android.systemui.Dumpable
-import com.android.systemui.animation.Interpolators
-import com.android.systemui.animation.InterpolatorsAndroidX
+import com.android.app.animation.Interpolators
+import com.android.app.animation.InterpolatorsAndroidX
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
index 5d07cac78682..57d20246ea14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/PropertyAnimator.java
@@ -24,7 +24,7 @@ import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
-import com.android.systemui.animation.Interpolators;
+import com.android.app.animation.Interpolators;
import com.android.systemui.statusbar.notification.stack.AnimationFilter;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ViewState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index 6deef2e11828..76ff97ddb61b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -383,11 +383,11 @@ class RoundableState(
}
fun debugString() = buildString {
- append("TargetView: ${targetView.hashCode()} ")
- append("Top: $topRoundness ")
- append(topRoundnessMap.map { "${it.key} ${it.value}" })
- append(" Bottom: $bottomRoundness ")
- append(bottomRoundnessMap.map { "${it.key} ${it.value}" })
+ append("Roundable { ")
+ append("top: { value: $topRoundness, requests: $topRoundnessMap}")
+ append(", ")
+ append("bottom: { value: $bottomRoundness, requests: $bottomRoundnessMap}")
+ append("}")
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
index 9f9fba437869..90eb630949fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/TransformState.java
@@ -23,11 +23,11 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
+import com.android.app.animation.Interpolators;
import com.android.internal.widget.MessagingImageMessage;
import com.android.internal.widget.MessagingPropertyAnimator;
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt
index dc1627438490..16f1a45ba83f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt
@@ -22,7 +22,7 @@ import android.animation.ValueAnimator
import android.view.View
import android.view.ViewGroup
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
/**
* Class to help with fading of view groups without fading one subview
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index 0529c94ed59a..23b5241b79ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -38,8 +38,7 @@ import com.android.systemui.statusbar.notification.collection.provider.LaunchFul
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.dagger.IncomingHeader
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
import com.android.systemui.statusbar.notification.logKey
import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
import com.android.systemui.statusbar.policy.HeadsUpManager
@@ -69,12 +68,12 @@ class HeadsUpCoordinator @Inject constructor(
private val mSystemClock: SystemClock,
private val mHeadsUpManager: HeadsUpManager,
private val mHeadsUpViewBinder: HeadsUpViewBinder,
- private val mNotificationInterruptStateProvider: NotificationInterruptStateProvider,
+ private val mVisualInterruptionDecisionProvider: VisualInterruptionDecisionProvider,
private val mRemoteInputManager: NotificationRemoteInputManager,
private val mLaunchFullScreenIntentProvider: LaunchFullScreenIntentProvider,
private val mFlags: NotifPipelineFlags,
@IncomingHeader private val mIncomingHeaderController: NodeController,
- @Main private val mExecutor: DelayableExecutor,
+ @Main private val mExecutor: DelayableExecutor
) : Coordinator {
private val mEntriesBindingUntil = ArrayMap<String, Long>()
private val mEntriesUpdateTimes = ArrayMap<String, Long>()
@@ -388,18 +387,21 @@ class HeadsUpCoordinator @Inject constructor(
override fun onEntryAdded(entry: NotificationEntry) {
// First check whether this notification should launch a full screen intent, and
// launch it if needed.
- val fsiDecision = mNotificationInterruptStateProvider.getFullScreenIntentDecision(entry)
- mNotificationInterruptStateProvider.logFullScreenIntentDecision(entry, fsiDecision)
- if (fsiDecision.shouldLaunch) {
+ val fsiDecision =
+ mVisualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision(entry)
+ mVisualInterruptionDecisionProvider.logFullScreenIntentDecision(fsiDecision)
+ if (fsiDecision.shouldInterrupt) {
mLaunchFullScreenIntentProvider.launchFullScreenIntent(entry)
- } else if (fsiDecision == FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND) {
+ } else if (fsiDecision.wouldInterruptWithoutDnd) {
// If DND was the only reason this entry was suppressed, note it for potential
// reconsideration on later ranking updates.
addForFSIReconsideration(entry, mSystemClock.currentTimeMillis())
}
- // shouldHeadsUp includes check for whether this notification should be filtered
- val shouldHeadsUpEver = mNotificationInterruptStateProvider.shouldHeadsUp(entry)
+ // makeAndLogHeadsUpDecision includes check for whether this notification should be
+ // filtered
+ val shouldHeadsUpEver =
+ mVisualInterruptionDecisionProvider.makeAndLogHeadsUpDecision(entry).shouldInterrupt
mPostedEntries[entry.key] = PostedEntry(
entry,
wasAdded = true,
@@ -420,7 +422,8 @@ class HeadsUpCoordinator @Inject constructor(
* up again.
*/
override fun onEntryUpdated(entry: NotificationEntry) {
- val shouldHeadsUpEver = mNotificationInterruptStateProvider.shouldHeadsUp(entry)
+ val shouldHeadsUpEver =
+ mVisualInterruptionDecisionProvider.makeAndLogHeadsUpDecision(entry).shouldInterrupt
val shouldHeadsUpAgain = shouldHunAgain(entry)
val isAlerting = mHeadsUpManager.isAlerting(entry.key)
val isBinding = isEntryBinding(entry)
@@ -510,26 +513,26 @@ class HeadsUpCoordinator @Inject constructor(
// If any of these entries are no longer suppressed, launch the FSI now.
if (isCandidateForFSIReconsideration(entry)) {
val decision =
- mNotificationInterruptStateProvider.getFullScreenIntentDecision(entry)
- if (decision.shouldLaunch) {
+ mVisualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision(
+ entry
+ )
+ if (decision.shouldInterrupt) {
// Log both the launch of the full screen and also that this was via a
// ranking update, and finally revoke candidacy for FSI reconsideration
- mLogger.logEntryUpdatedToFullScreen(entry.key, decision.name)
- mNotificationInterruptStateProvider.logFullScreenIntentDecision(
- entry, decision)
+ mLogger.logEntryUpdatedToFullScreen(entry.key, decision.logReason)
+ mVisualInterruptionDecisionProvider.logFullScreenIntentDecision(decision)
mLaunchFullScreenIntentProvider.launchFullScreenIntent(entry)
mFSIUpdateCandidates.remove(entry.key)
// if we launch the FSI then this is no longer a candidate for HUN
continue
- } else if (decision == FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND) {
+ } else if (decision.wouldInterruptWithoutDnd) {
// decision has not changed; no need to log
} else {
// some other condition is now blocking FSI; log that and revoke candidacy
// for FSI reconsideration
- mLogger.logEntryDisqualifiedFromFullScreen(entry.key, decision.name)
- mNotificationInterruptStateProvider.logFullScreenIntentDecision(
- entry, decision)
+ mLogger.logEntryDisqualifiedFromFullScreen(entry.key, decision.logReason)
+ mVisualInterruptionDecisionProvider.logFullScreenIntentDecision(decision)
mFSIUpdateCandidates.remove(entry.key)
}
}
@@ -539,13 +542,18 @@ class HeadsUpCoordinator @Inject constructor(
// state
// - if it is present in PostedEntries and the previous state of shouldHeadsUp
// differs from the updated one
- val shouldHeadsUpEver = mNotificationInterruptStateProvider.checkHeadsUp(entry,
- /* log= */ false)
+ val decision =
+ mVisualInterruptionDecisionProvider.makeUnloggedHeadsUpDecision(entry)
+ val shouldHeadsUpEver = decision.shouldInterrupt
val postedShouldHeadsUpEver = mPostedEntries[entry.key]?.shouldHeadsUpEver ?: false
val shouldUpdateEntry = postedShouldHeadsUpEver != shouldHeadsUpEver
if (shouldUpdateEntry) {
- mLogger.logEntryUpdatedByRanking(entry.key, shouldHeadsUpEver)
+ mLogger.logEntryUpdatedByRanking(
+ entry.key,
+ shouldHeadsUpEver,
+ decision.logReason
+ )
onEntryUpdated(entry)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
index e9365594fad7..32c3c6665b6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
@@ -61,12 +61,13 @@ class HeadsUpCoordinatorLogger constructor(
})
}
- fun logEntryUpdatedByRanking(key: String, shouldHun: Boolean) {
+ fun logEntryUpdatedByRanking(key: String, shouldHun: Boolean, reason: String) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = key
bool1 = shouldHun
+ str2 = reason
}, {
- "updating entry via ranking applied: $str1 updated shouldHeadsUp=$bool1"
+ "updating entry via ranking applied: $str1 updated shouldHeadsUp=$bool1 because $str2"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index a352f23bfc1c..115e050258c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.interruption
+import android.util.Log
+
import com.android.systemui.log.dagger.NotificationInterruptLog
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogLevel.DEBUG
@@ -23,6 +25,7 @@ import com.android.systemui.plugins.log.LogLevel.INFO
import com.android.systemui.plugins.log.LogLevel.WARNING
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
+import com.android.systemui.util.Compile
import javax.inject.Inject
class NotificationInterruptLogger @Inject constructor(
@@ -44,11 +47,13 @@ class NotificationInterruptLogger @Inject constructor(
}
fun logNoBubbleNotAllowed(entry: NotificationEntry) {
- buffer.log(TAG, DEBUG, {
- str1 = entry.logKey
- }, {
- "No bubble up: not allowed to bubble: $str1"
- })
+ if (Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG)) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
+ }, {
+ "No bubble up: not allowed to bubble: $str1"
+ })
+ }
}
fun logNoBubbleNoMetadata(entry: NotificationEntry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
index f2216fce6fef..ebba4b1aa265 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapper.kt
@@ -36,6 +36,8 @@ class NotificationInterruptStateProviderWrapper(
SHOULD_INTERRUPT(shouldInterrupt = true),
SHOULD_NOT_INTERRUPT(shouldInterrupt = false);
+ override val logReason = "unknown"
+
companion object {
fun of(booleanDecision: Boolean) =
if (booleanDecision) SHOULD_INTERRUPT else SHOULD_NOT_INTERRUPT
@@ -49,6 +51,7 @@ class NotificationInterruptStateProviderWrapper(
) : FullScreenIntentDecision {
override val shouldInterrupt = originalDecision.shouldLaunch
override val wouldInterruptWithoutDnd = originalDecision == NO_FSI_SUPPRESSED_ONLY_BY_DND
+ override val logReason = originalDecision.name
}
override fun addSuppressor(suppressor: NotificationInterruptSuppressor) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
index c0f4fcda56bb..8024016fd3fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
@@ -32,9 +32,12 @@ interface VisualInterruptionDecisionProvider {
* full-screen intent decisions.
*
* @property[shouldInterrupt] whether a visual interruption should be triggered
+ * @property[logReason] a log-friendly string explaining the reason for the decision; should be
+ * used *only* for logging, not decision-making
*/
interface Decision {
val shouldInterrupt: Boolean
+ val logReason: String
}
/**
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 766ad88f8a55..f70d5e69afe6 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
@@ -31,12 +31,12 @@ import android.view.View;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import com.android.app.animation.Interpolators;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.jank.InteractionJankMonitor.Configuration;
import com.android.settingslib.Utils;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
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 10cdae3b967c..2695410c8ac4 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
@@ -64,6 +64,7 @@ import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -72,7 +73,6 @@ import com.android.internal.util.ContrastColorUtil;
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.CallLayout;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -3649,16 +3649,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
} else {
pw.println("no viewState!!!");
}
- pw.println("Roundness: " + getRoundableState().debugString());
+ pw.println(getRoundableState().debugString());
int transientViewCount = mChildrenContainer == null
? 0 : mChildrenContainer.getTransientViewCount();
if (mIsSummaryWithChildren || transientViewCount > 0) {
- pw.println();
- pw.print("ChildrenContainer");
- pw.print(" visibility: " + mChildrenContainer.getVisibility());
- pw.print(", alpha: " + mChildrenContainer.getAlpha());
- pw.print(", translationY: " + mChildrenContainer.getTranslationY());
+ pw.println(mChildrenContainer.debugString());
pw.println();
List<ExpandableNotificationRow> notificationChildren = getAttachedChildren();
pw.print("Children: " + notificationChildren.size() + " {");
@@ -3726,12 +3722,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
@Override
- public String toString() {
- String roundableStateDebug = "RoundableState = " + getRoundableState().debugString();
- return "ExpandableNotificationRow:" + hashCode() + " { " + roundableStateDebug + " }";
- }
-
- @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mUseRoundnessSourceTypes) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
index b56bae12be6c..7a2bee91e972 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -45,10 +45,10 @@ import android.widget.Toast;
import androidx.annotation.VisibleForTesting;
+import com.android.app.animation.Interpolators;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 197caa2d5645..9aa50e989ff1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -352,7 +352,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
super.dump(pw, args);
DumpUtilsKt.withIncreasedIndent(pw, () -> {
- pw.println("Roundness: " + getRoundableState().debugString());
+ pw.println(getRoundableState().debugString());
if (DUMP_VERBOSE) {
pw.println("mCustomOutline: " + mCustomOutline + " mOutlineRect: " + mOutlineRect);
pw.println("mOutlineAlpha: " + mOutlineAlpha);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 5edff5f4e5d2..9dbbc5874f28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -32,9 +32,9 @@ import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.Roundable;
import com.android.systemui.statusbar.notification.RoundableState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 49f17b664a20..6bbeebfdb431 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -17,8 +17,6 @@
package com.android.systemui.statusbar.notification.row;
import android.annotation.ColorInt;
-import android.annotation.DrawableRes;
-import android.annotation.StringRes;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -50,8 +48,8 @@ public class FooterView extends StackScrollerDecorView {
// Footer label
private TextView mSeenNotifsFooterTextView;
- private @StringRes int mSeenNotifsFilteredText;
- private int mUnlockIconSize;
+ private String mSeenNotifsFilteredText;
+ private Drawable mSeenNotifsFilteredIcon;
public FooterView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -87,30 +85,12 @@ public class FooterView extends StackScrollerDecorView {
mManageButton = findViewById(R.id.manage_text);
mSeenNotifsFooterTextView = findViewById(R.id.unlock_prompt_footer);
updateResources();
- updateText();
+ updateContent();
updateColors();
}
- public void setFooterLabelTextAndIcon(@StringRes int text, @DrawableRes int icon) {
- mSeenNotifsFilteredText = text;
- if (mSeenNotifsFilteredText != 0) {
- mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText);
- } else {
- mSeenNotifsFooterTextView.setText(null);
- }
- Drawable drawable;
- if (icon == 0) {
- drawable = null;
- } else {
- drawable = getResources().getDrawable(icon);
- drawable.setBounds(0, 0, mUnlockIconSize, mUnlockIconSize);
- }
- mSeenNotifsFooterTextView.setCompoundDrawablesRelative(drawable, null, null, null);
- updateFooterVisibilityMode();
- }
-
- private void updateFooterVisibilityMode() {
- if (mSeenNotifsFilteredText != 0) {
+ public void setFooterLabelVisible(boolean isVisible) {
+ if (isVisible) {
mManageButton.setVisibility(View.GONE);
mClearAllButton.setVisibility(View.GONE);
mSeenNotifsFooterTextView.setVisibility(View.VISIBLE);
@@ -141,10 +121,10 @@ public class FooterView extends StackScrollerDecorView {
return;
}
mShowHistory = showHistory;
- updateText();
+ updateContent();
}
- private void updateText() {
+ private void updateContent() {
if (mShowHistory) {
mManageButton.setText(mManageNotificationHistoryText);
mManageButton.setContentDescription(mManageNotificationHistoryText);
@@ -152,6 +132,9 @@ public class FooterView extends StackScrollerDecorView {
mManageButton.setText(mManageNotificationText);
mManageButton.setContentDescription(mManageNotificationText);
}
+ mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText);
+ mSeenNotifsFooterTextView
+ .setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null);
}
public boolean isHistoryShown() {
@@ -166,7 +149,7 @@ public class FooterView extends StackScrollerDecorView {
mClearAllButton.setContentDescription(
mContext.getString(R.string.accessibility_clear_all));
updateResources();
- updateText();
+ updateContent();
}
/**
@@ -190,8 +173,11 @@ public class FooterView extends StackScrollerDecorView {
mManageNotificationText = getContext().getString(R.string.manage_notifications_text);
mManageNotificationHistoryText = getContext()
.getString(R.string.manage_notifications_history_text);
- mUnlockIconSize = getResources()
+ int unlockIconSize = getResources()
.getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size);
+ mSeenNotifsFilteredText = getContext().getString(R.string.unlock_to_see_notif_text);
+ mSeenNotifsFilteredIcon = getContext().getDrawable(R.drawable.ic_friction_lock_closed);
+ mSeenNotifsFilteredIcon.setBounds(0, 0, unlockIconSize, unlockIconSize);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index f21db0bde59a..9bc03336c3b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -25,7 +25,7 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
-import static com.android.systemui.animation.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
import static java.lang.annotation.RetentionPolicy.SOURCE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index 596bdc09efe4..047db2046529 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -33,9 +33,9 @@ import android.widget.FrameLayout;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 8a50f2f527fa..99a77550cc76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -20,7 +20,7 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
-import static com.android.systemui.animation.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
import static java.lang.annotation.RetentionPolicy.SOURCE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index bafc474d7123..5a129fccff06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -39,9 +39,9 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index 5f4c9267ee4a..d5d7f75fbaa1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -45,11 +45,11 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index 5aaf63f8d32d..b24cec150941 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -23,8 +23,8 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Interpolator;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.animation.Interpolators;
import java.util.function.Consumer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 9a777ea6230b..84fe9ef6f1ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -34,10 +34,10 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.TransformableView;
import com.android.systemui.statusbar.ViewTransformationHelper;
import com.android.systemui.statusbar.notification.CustomInterpolatorTransformation;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index c82318913ced..99c6ddb8cfaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -33,12 +33,9 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.phone.NotificationIconAreaController
import com.android.systemui.statusbar.phone.NotificationIconContainer
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
-import com.android.systemui.util.kotlin.getValue
-import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
/**
* Controller class for [NotificationShelf]. This implementation serves as a temporary wrapper
@@ -47,39 +44,12 @@ import kotlinx.coroutines.flow.onEach
* removed, this class can go away and the ViewBinder can be used directly.
*/
@CentralSurfacesScope
-class NotificationShelfViewBinderWrapperControllerImpl
-@Inject
-constructor(
- private val shelf: NotificationShelf,
- private val viewModel: NotificationShelfViewModel,
- featureFlags: FeatureFlags,
- private val falsingManager: FalsingManager,
- hostControllerLazy: Lazy<NotificationStackScrollLayoutController>,
- private val notificationIconAreaController: NotificationIconAreaController,
-) : NotificationShelfController {
-
- private val hostController: NotificationStackScrollLayoutController by hostControllerLazy
+class NotificationShelfViewBinderWrapperControllerImpl @Inject constructor() :
+ NotificationShelfController {
override val view: NotificationShelf
get() = unsupported
- init {
- shelf.apply {
- setRefactorFlagEnabled(featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR))
- useRoundnessSourceTypes(featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES))
- setSensitiveRevealAnimEndabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM))
- }
- }
-
- fun init() {
- NotificationShelfViewBinder.bind(viewModel, shelf, falsingManager)
- hostController.setShelf(shelf)
- hostController.setOnNotificationRemovedListener { child, _ ->
- view.requestRoundnessResetFor(child)
- }
- notificationIconAreaController.setShelfIcons(shelf.shelfIcons)
- }
-
override val intrinsicHeight: Int
get() = unsupported
@@ -99,21 +69,32 @@ constructor(
get() = NotificationShelfController.throwIllegalFlagStateError(expected = true)
}
-/** Binds a [NotificationShelf] to its backend. */
+/** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */
object NotificationShelfViewBinder {
fun bind(
- viewModel: NotificationShelfViewModel,
shelf: NotificationShelf,
+ viewModel: NotificationShelfViewModel,
falsingManager: FalsingManager,
+ featureFlags: FeatureFlags,
+ notificationIconAreaController: NotificationIconAreaController,
) {
ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
- shelf.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- viewModel.canModifyColorOfNotifications
- .onEach(shelf::setCanModifyColorOfNotifications)
- .launchIn(this)
- viewModel.isClickable.onEach(shelf::setCanInteract).launchIn(this)
- registerViewListenersWhileAttached(shelf, viewModel)
+ shelf.apply {
+ setRefactorFlagEnabled(true)
+ useRoundnessSourceTypes(featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES))
+ setSensitiveRevealAnimEndabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM))
+ // TODO(278765923): Replace with eventual NotificationIconContainerViewBinder#bind()
+ notificationIconAreaController.setShelfIcons(shelfIcons)
+ repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.canModifyColorOfNotifications.collect(
+ ::setCanModifyColorOfNotifications
+ )
+ }
+ launch { viewModel.isClickable.collect(::setCanInteract) }
+ registerViewListenersWhileAttached(shelf, viewModel)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
index 7f3381ccd38a..d73bbebe40b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ExpandableViewState.java
@@ -22,8 +22,8 @@ import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.view.View;
+import com.android.app.animation.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 40f55bd3726c..160a2309bfcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -1546,9 +1546,11 @@ public class NotificationChildrenContainer extends ViewGroup
mUseRoundnessSourceTypes = enabled;
}
- @Override
- public String toString() {
- String roundableStateDebug = "RoundableState = " + getRoundableState().debugString();
- return "NotificationChildrenContainer:" + hashCode() + " { " + roundableStateDebug + " }";
+ public String debugString() {
+ return TAG + " { "
+ + "visibility: " + getVisibility()
+ + ", alpha: " + getAlpha()
+ + ", translationY: " + getTranslationY()
+ + ", roundableState: " + getRoundableState().debugString() + "}";
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java
index 0b435fe9dcc6..9a33a9440602 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java
@@ -26,7 +26,7 @@ import android.graphics.Rect;
import android.view.View;
import android.view.animation.Interpolator;
-import com.android.systemui.animation.Interpolators;
+import com.android.app.animation.Interpolators;
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.row.ExpandableView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 555d5027c7b6..24e8f39cb4d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -75,6 +75,7 @@ import android.view.animation.Interpolator;
import android.widget.OverScroller;
import android.widget.ScrollView;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.internal.jank.InteractionJankMonitor;
@@ -86,7 +87,6 @@ import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -2839,6 +2839,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* @param listener callback for notification removed
*/
public void setOnNotificationRemovedListener(OnNotificationRemovedListener listener) {
+ NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags());
mOnNotificationRemovedListener = listener;
}
@@ -2852,10 +2853,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (!mChildTransferInProgress) {
onViewRemovedInternal(expandableView, this);
}
- if (mOnNotificationRemovedListener != null) {
- mOnNotificationRemovedListener.onNotificationRemoved(
- expandableView,
- mChildTransferInProgress);
+ if (mAmbientState.getFeatureFlags().isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ mShelf.requestRoundnessResetFor(expandableView);
+ } else {
+ if (mOnNotificationRemovedListener != null) {
+ mOnNotificationRemovedListener.onNotificationRemoved(
+ expandableView,
+ mChildTransferInProgress);
+ }
}
}
@@ -4741,13 +4746,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mFooterView.setVisible(visible, animate);
mFooterView.setSecondaryVisible(showDismissView, animate);
mFooterView.showHistory(showHistory);
- if (mHasFilteredOutSeenNotifications) {
- mFooterView.setFooterLabelTextAndIcon(
- R.string.unlock_to_see_notif_text,
- R.drawable.ic_friction_lock_closed);
- } else {
- mFooterView.setFooterLabelTextAndIcon(0, 0);
- }
+ mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications);
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
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 9979cc4fa9d5..b69ce3861342 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
@@ -105,11 +105,14 @@ import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
+import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -122,16 +125,17 @@ import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.Compile;
import com.android.systemui.util.settings.SecureSettings;
+import kotlin.Unit;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Named;
-import kotlin.Unit;
-
/**
* Controller for {@link NotificationStackScrollLayout}.
*/
@@ -151,6 +155,8 @@ public class NotificationStackScrollLayoutController {
private final ConfigurationController mConfigurationController;
private final ZenModeController mZenModeController;
private final MetricsLogger mMetricsLogger;
+ private final Optional<NotificationListViewModel> mViewModel;
+
private final DumpManager mDumpManager;
private final FalsingCollector mFalsingCollector;
private final FalsingManager mFalsingManager;
@@ -175,6 +181,8 @@ public class NotificationStackScrollLayoutController {
private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
private final StackStateLogger mStackStateLogger;
private final NotificationStackScrollLogger mLogger;
+ private final NotificationIconAreaController mNotifIconAreaController;
+
private final GroupExpansionManager mGroupExpansionManager;
private final NotifPipelineFlags mNotifPipelineFlags;
private final SeenNotificationsProvider mSeenNotificationsProvider;
@@ -642,6 +650,7 @@ public class NotificationStackScrollLayoutController {
KeyguardBypassController keyguardBypassController,
ZenModeController zenModeController,
NotificationLockscreenUserManager lockscreenUserManager,
+ Optional<NotificationListViewModel> nsslViewModel,
MetricsLogger metricsLogger,
DumpManager dumpManager,
FalsingCollector falsingCollector,
@@ -665,6 +674,7 @@ public class NotificationStackScrollLayoutController {
StackStateLogger stackLogger,
NotificationStackScrollLogger logger,
NotificationStackSizeCalculator notificationStackSizeCalculator,
+ NotificationIconAreaController notifIconAreaController,
FeatureFlags featureFlags,
NotificationTargetsHelper notificationTargetsHelper,
SecureSettings secureSettings,
@@ -686,6 +696,7 @@ public class NotificationStackScrollLayoutController {
mKeyguardBypassController = keyguardBypassController;
mZenModeController = zenModeController;
mLockscreenUserManager = lockscreenUserManager;
+ mViewModel = nsslViewModel;
mMetricsLogger = metricsLogger;
mDumpManager = dumpManager;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
@@ -707,6 +718,7 @@ public class NotificationStackScrollLayoutController {
mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator;
mSeenNotificationsProvider = seenNotificationsProvider;
mShadeController = shadeController;
+ mNotifIconAreaController = notifIconAreaController;
mFeatureFlags = featureFlags;
mUseRoundnessSourceTypes = featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES);
mNotificationTargetsHelper = notificationTargetsHelper;
@@ -820,6 +832,10 @@ public class NotificationStackScrollLayoutController {
mGroupExpansionManager.registerGroupExpansionChangeListener(
(changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
+
+ mViewModel.ifPresent(
+ vm -> NotificationListViewBinder
+ .bind(mView, vm, mFalsingManager, mFeatureFlags, mNotifIconAreaController));
}
private boolean isInVisibleLocation(NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index ee72943bef57..f07dd00827b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -22,9 +22,9 @@ import android.animation.ValueAnimator;
import android.util.Property;
import android.view.View;
+import com.android.app.animation.Interpolators;
import com.android.keyguard.KeyguardSliceView;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
index d07da381a186..f4605be2b9c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ViewState.java
@@ -26,9 +26,9 @@ import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
+import com.android.app.animation.Interpolators;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.NotificationFadeAware.FadeOptimizedNotification;
import com.android.systemui.statusbar.notification.PropertyAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
new file mode 100644
index 000000000000..45ae4e0afc3a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.statusbar.notification.stack.ui.viewbinder
+
+import android.view.LayoutInflater
+import com.android.systemui.R
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.NotificationShelf
+import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
+import com.android.systemui.statusbar.phone.NotificationIconAreaController
+
+/** Binds a [NotificationStackScrollLayout] to its [view model][NotificationListViewModel]. */
+object NotificationListViewBinder {
+ @JvmStatic
+ fun bind(
+ view: NotificationStackScrollLayout,
+ viewModel: NotificationListViewModel,
+ falsingManager: FalsingManager,
+ featureFlags: FeatureFlags,
+ iconAreaController: NotificationIconAreaController,
+ ) {
+ val shelf =
+ LayoutInflater.from(view.context)
+ .inflate(R.layout.status_bar_notification_shelf, view, false) as NotificationShelf
+ NotificationShelfViewBinder.bind(
+ shelf,
+ viewModel.shelf,
+ falsingManager,
+ featureFlags,
+ iconAreaController
+ )
+ view.setShelf(shelf)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
new file mode 100644
index 000000000000..aab1c2b877e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.statusbar.notification.stack.ui.viewmodel
+
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
+import dagger.Module
+import dagger.Provides
+import java.util.Optional
+import javax.inject.Provider
+
+/** ViewModel for the list of notifications. */
+class NotificationListViewModel(
+ val shelf: NotificationShelfViewModel,
+)
+
+@Module
+object NotificationListViewModelModule {
+ @JvmStatic
+ @Provides
+ fun maybeProvideViewModel(
+ featureFlags: FeatureFlags,
+ shelfViewModel: Provider<NotificationShelfViewModel>,
+ ): Optional<NotificationListViewModel> =
+ if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+ Optional.of(NotificationListViewModel(shelfViewModel.get()))
+ } else {
+ Optional.empty()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 9dce332985f9..459071280a1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -33,9 +33,9 @@ import android.os.SystemClock;
import android.util.Log;
import android.view.View;
+import com.android.app.animation.Interpolators;
import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
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 b547b4c73a91..2387495de69b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -452,7 +452,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
protected PhoneStatusBarView mStatusBarView;
private PhoneStatusBarViewController mPhoneStatusBarViewController;
private PhoneStatusBarTransitions mStatusBarTransitions;
- private AuthRippleController mAuthRippleController;
+ private final AuthRippleController mAuthRippleController;
@WindowVisibleState private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarInitializer mStatusBarInitializer;
@@ -460,7 +460,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@VisibleForTesting
DozeServiceHost mDozeServiceHost;
- private LightRevealScrim mLightRevealScrim;
+ private final LightRevealScrim mLightRevealScrim;
private PowerButtonReveal mPowerButtonReveal;
private boolean mWakeUpComingFromTouch;
@@ -768,6 +768,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
ScrimController scrimController,
Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
+ AuthRippleController authRippleController,
DozeServiceHost dozeServiceHost,
PowerManager powerManager,
ScreenPinningRequest screenPinningRequest,
@@ -810,6 +811,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
IDreamManager dreamManager,
Lazy<CameraLauncher> cameraLauncherLazy,
Lazy<LightRevealScrimViewModel> lightRevealScrimViewModelLazy,
+ LightRevealScrim lightRevealScrim,
AlternateBouncerInteractor alternateBouncerInteractor,
UserTracker userTracker,
Provider<FingerprintManager> fingerprintManager
@@ -866,6 +868,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mScreenPinningRequest = screenPinningRequest;
mDozeScrimController = dozeScrimController;
mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
+ mAuthRippleController = authRippleController;
mNotificationShadeDepthControllerLazy = notificationShadeDepthControllerLazy;
mVolumeComponent = volumeComponent;
mCommandQueue = commandQueue;
@@ -932,6 +935,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
wiredChargingRippleController.registerCallbacks();
mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy;
+ mLightRevealScrim = lightRevealScrim;
// Based on teamfood flag, turn predictive back dispatch on at runtime.
if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
@@ -1326,8 +1330,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
});
mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront);
- mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
-
if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
LightRevealScrimViewBinder.bind(
mLightRevealScrim, mLightRevealScrimViewModelLazy.get());
@@ -1664,8 +1666,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mPresenter = mCentralSurfacesComponent.getNotificationPresenter();
mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter();
mNotificationShelfController = mCentralSurfacesComponent.getNotificationShelfController();
- mAuthRippleController = mCentralSurfacesComponent.getAuthRippleController();
- mAuthRippleController.init();
mHeadsUpManager.addListener(mCentralSurfacesComponent.getStatusBarHeadsUpChangeListener());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 90a6d0fac7ca..c1859b2ee3a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -23,10 +23,10 @@ import static com.android.systemui.statusbar.notification.NotificationUtils.inte
import android.content.res.Resources;
import android.util.MathUtils;
+import com.android.app.animation.Interpolators;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 9d30cb4c4852..61c1cc82482a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -30,9 +30,9 @@ import android.widget.TextView;
import androidx.annotation.StyleRes;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.keyguard.KeyguardIndication;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 13566ef8c630..720eeba0fd4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -43,9 +43,9 @@ import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
+import com.android.app.animation.Interpolators;
import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index e835c5cebbc3..5232fb633e63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -37,12 +37,12 @@ import androidx.core.animation.Animator;
import androidx.core.animation.AnimatorListenerAdapter;
import androidx.core.animation.ValueAnimator;
+import com.android.app.animation.InterpolatorsAndroidX;
import com.android.keyguard.CarrierTextController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.R;
-import com.android.systemui.animation.InterpolatorsAndroidX;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.log.LogLevel;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 6bf54430ab38..7bc4fc3c5e47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -24,21 +24,21 @@ import android.os.SystemClock;
import android.util.MathUtils;
import android.util.TimeUtils;
+import com.android.app.animation.Interpolators;
import com.android.systemui.Dumpable;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+
/**
* Class to control all aspects about light bar changes.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index cc4f901668ab..46a2457670b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -31,9 +31,9 @@ import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.ViewTreeObserver.OnPreDrawListener;
+import com.android.app.animation.Interpolators;
import com.android.internal.graphics.ColorUtils;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import java.lang.annotation.Retention;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 55dc18859c25..560ea8aae594 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -15,11 +15,11 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.collection.ArrayMap;
+import com.android.app.animation.Interpolators;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 006a029de8e0..bef422ce3004 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -36,10 +36,10 @@ import android.view.animation.Interpolator;
import androidx.annotation.VisibleForTesting;
import androidx.collection.ArrayMap;
+import com.android.app.animation.Interpolators;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.settingslib.Utils;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.stack.AnimationFilter;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index 5e5317d764fe..07a6d0a5a470 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -29,7 +29,7 @@ import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
-import com.android.systemui.animation.Interpolators;
+import com.android.app.animation.Interpolators;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
public class SettingsButton extends AlphaOptimizedImageView {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 8fa803ea3a8f..cdf66526ab63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -15,7 +15,7 @@ import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
import com.android.systemui.DejankUtils
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
index e908a4b475c9..ddb6d93e3c4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
@@ -112,11 +112,6 @@ public interface CentralSurfacesComponent {
LockIconViewController getLockIconViewController();
/**
- * Creates an AuthRippleViewController. Must be init after creation.
- */
- AuthRippleController getAuthRippleController();
-
- /**
* Creates a StatusBarHeadsUpChangeListener.
*/
StatusBarHeadsUpChangeListener getStatusBarHeadsUpChangeListener();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 95d36b082b57..1a943e79a79b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -29,7 +29,6 @@ import com.android.keyguard.LockIconView;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.battery.BatteryMeterViewController;
-import com.android.systemui.biometrics.AuthRippleView;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
@@ -55,6 +54,7 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfC
import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule;
import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModelModule;
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
@@ -87,7 +87,10 @@ import javax.inject.Named;
import javax.inject.Provider;
@Module(subcomponents = StatusBarFragmentComponent.class,
- includes = { ActivatableNotificationViewModelModule.class })
+ includes = {
+ ActivatableNotificationViewModelModule.class,
+ NotificationListViewModelModule.class,
+ })
public abstract class StatusBarViewModule {
public static final String SHADE_HEADER = "large_screen_shade_header";
@@ -117,9 +120,7 @@ public abstract class StatusBarViewModule {
NotificationShelfComponent.Builder notificationShelfComponentBuilder,
NotificationShelf notificationShelf) {
if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
- NotificationShelfViewBinderWrapperControllerImpl impl = newImpl.get();
- impl.init();
- return impl;
+ return newImpl.get();
} else {
NotificationShelfComponent component = notificationShelfComponentBuilder
.notificationShelf(notificationShelf)
@@ -148,15 +149,6 @@ public abstract class StatusBarViewModule {
/** */
@Provides
- @CentralSurfacesComponent.CentralSurfacesScope
- @Nullable
- public static AuthRippleView getAuthRippleView(
- NotificationShadeWindowView notificationShadeWindowView) {
- return notificationShadeWindowView.findViewById(R.id.auth_ripple);
- }
-
- /** */
- @Provides
@Named(SHADE_HEADER)
@CentralSurfacesComponent.CentralSurfacesScope
public static MotionLayout getLargeScreenShadeHeaderBarView(
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 453dd1bb6f81..831d40259cb5 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
@@ -44,10 +44,10 @@ import android.widget.LinearLayout;
import androidx.annotation.VisibleForTesting;
import androidx.core.animation.Animator;
+import com.android.app.animation.Interpolators;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt
index 16e176613ec9..be2e41a2a581 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt
@@ -45,10 +45,6 @@ data class SignalIconModel(
}
companion object {
- /** Creates a [SignalIconModel] representing an empty and invalidated state. */
- fun createEmptyState(numberOfLevels: Int) =
- SignalIconModel(level = 0, numberOfLevels, showExclamationMark = true)
-
private const val COL_LEVEL = "level"
private const val COL_NUM_LEVELS = "numLevels"
private const val COL_SHOW_EXCLAMATION = "showExclamation"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index bfd133e6830c..54730ed82564 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
-import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH_NONE
import com.android.settingslib.graph.SignalDrawable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -78,13 +77,24 @@ constructor(
scope: CoroutineScope,
) : MobileIconViewModelCommon {
/** Whether or not to show the error state of [SignalDrawable] */
- private val showExclamationMark: Flow<Boolean> =
+ private val showExclamationMark: StateFlow<Boolean> =
combine(
- iconInteractor.isDefaultDataEnabled,
- iconInteractor.isDefaultConnectionFailed,
- ) { isDefaultDataEnabled, isDefaultConnectionFailed ->
- !isDefaultDataEnabled || isDefaultConnectionFailed
- }
+ iconInteractor.isDefaultDataEnabled,
+ iconInteractor.isDefaultConnectionFailed,
+ iconInteractor.isInService,
+ ) { isDefaultDataEnabled, isDefaultConnectionFailed, isInService ->
+ !isDefaultDataEnabled || isDefaultConnectionFailed || !isInService
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), true)
+
+ private val shownLevel: StateFlow<Int> =
+ combine(
+ iconInteractor.level,
+ iconInteractor.isInService,
+ ) { level, isInService ->
+ if (isInService) level else 0
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
override val isVisible: StateFlow<Boolean> =
if (!constants.hasDataCapabilities) {
@@ -107,18 +117,18 @@ constructor(
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val icon: Flow<SignalIconModel> = run {
- val initial = SignalIconModel.createEmptyState(iconInteractor.numberOfLevels.value)
+ val initial =
+ SignalIconModel(
+ level = shownLevel.value,
+ numberOfLevels = iconInteractor.numberOfLevels.value,
+ showExclamationMark = showExclamationMark.value,
+ )
combine(
- iconInteractor.level,
+ shownLevel,
iconInteractor.numberOfLevels,
showExclamationMark,
- iconInteractor.isInService,
- ) { level, numberOfLevels, showExclamationMark, isInService ->
- if (!isInService) {
- SignalIconModel.createEmptyState(numberOfLevels)
- } else {
- SignalIconModel(level, numberOfLevels, showExclamationMark)
- }
+ ) { shownLevel, numberOfLevels, showExclamationMark ->
+ SignalIconModel(shownLevel, numberOfLevels, showExclamationMark)
}
.distinctUntilChanged()
.logDiffsForTable(
@@ -130,19 +140,9 @@ constructor(
}
override val contentDescription: Flow<ContentDescription> = run {
- val initial = ContentDescription.Resource(PHONE_SIGNAL_STRENGTH_NONE)
- combine(
- iconInteractor.level,
- iconInteractor.isInService,
- ) { level, isInService ->
- val resId =
- when {
- isInService -> PHONE_SIGNAL_STRENGTH[level]
- else -> PHONE_SIGNAL_STRENGTH_NONE
- }
- ContentDescription.Resource(resId)
- }
- .distinctUntilChanged()
+ val initial = ContentDescription.Resource(PHONE_SIGNAL_STRENGTH[0])
+ shownLevel
+ .map { ContentDescription.Resource(PHONE_SIGNAL_STRENGTH[it]) }
.stateIn(scope, SharingStarted.WhileSubscribed(), initial)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index b37c44a2f8cd..4e52be91f0af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -160,8 +160,10 @@ constructor(
val wifi = currentWifi
if (
- wifi is WifiNetworkModel.Active &&
- wifi.networkId == network.getNetId()
+ (wifi is WifiNetworkModel.Active &&
+ wifi.networkId == network.getNetId()) ||
+ (wifi is WifiNetworkModel.CarrierMerged &&
+ wifi.networkId == network.getNetId())
) {
val newNetworkModel = WifiNetworkModel.Inactive
currentWifi = newNetworkModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
index 4dd63be47735..e1ec94fada81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserDetailItemView.java
@@ -24,9 +24,9 @@ import android.view.View;
import androidx.core.graphics.ColorUtils;
+import com.android.app.animation.Interpolators;
import com.android.keyguard.KeyguardConstants;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.qs.tiles.UserDetailItemView;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
index 928e0115287d..66b52563c0a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -31,6 +31,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import com.android.app.animation.Interpolators;
import com.android.keyguard.KeyguardConstants;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -38,7 +39,6 @@ import com.android.keyguard.KeyguardVisibilityHelper;
import com.android.keyguard.dagger.KeyguardUserSwitcherScope;
import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
index 850a4b499562..363b06ab780b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherListView.java
@@ -21,11 +21,11 @@ import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
+import com.android.app.animation.Interpolators;
import com.android.keyguard.AlphaOptimizedLinearLayout;
import com.android.keyguard.KeyguardConstants;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
-import com.android.systemui.animation.Interpolators;
/**
* The container for the user switcher on Keyguard.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 403a7e8116c8..e311bad9e865 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -73,6 +73,7 @@ import androidx.core.animation.AnimatorSet;
import androidx.core.animation.ObjectAnimator;
import androidx.core.animation.ValueAnimator;
+import com.android.app.animation.InterpolatorsAndroidX;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.internal.logging.UiEvent;
@@ -80,7 +81,6 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.animation.InterpolatorsAndroidX;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt
index 16123882046c..46954b5b81ef 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt
@@ -18,7 +18,7 @@ package com.android.systemui.temporarydisplay.chipbar
import android.view.View
import android.view.ViewGroup
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
import com.android.systemui.animation.ViewHierarchyAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.util.children
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index e819f946a6d6..4fbbc8915c19 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -34,10 +34,10 @@ import android.widget.TextView
import androidx.annotation.DimenRes
import androidx.annotation.IdRes
import androidx.annotation.VisibleForTesting
+import com.android.app.animation.Interpolators
import com.android.internal.widget.CachingIconView
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
-import com.android.systemui.animation.Interpolators
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Text.Companion.loadText
diff --git a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
index 64234c205617..41c6b5d8493b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
@@ -16,20 +16,22 @@
package com.android.systemui.util
-import android.os.Trace
+import android.os.Handler
import android.os.TraceNameSupplier
+import androidx.tracing.Trace
/**
- * Run a block within a [Trace] section.
- * Calls [Trace.beginSection] before and [Trace.endSection] after the passed block.
+ * Run a block within a [Trace] section. Calls [Trace.beginSection] before and [Trace.endSection]
+ * after the passed block. If tracing is disabled, it will run the block directly to avoid using an
+ * unnecessary try-finally block.
*/
inline fun <T> traceSection(tag: String, block: () -> T): T =
- if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) {
- Trace.traceBegin(Trace.TRACE_TAG_APP, tag)
+ if (Trace.isEnabled()) {
+ Trace.beginSection(tag)
try {
block()
} finally {
- Trace.traceEnd(Trace.TRACE_TAG_APP)
+ Trace.endSection()
}
} else {
block()
@@ -42,8 +44,10 @@ class TraceUtils {
}
/**
- * Helper function for creating a Runnable object that implements TraceNameSupplier.
- * This is useful for posting Runnables to Handlers with meaningful names.
+ * Helper function for creating a [Runnable] that implements [TraceNameSupplier]. This is
+ * useful when posting to a [Handler] so that the [Runnable] has a meaningful name in the
+ * trace. Otherwise, the class name of the [Runnable] is used, which is often something like
+ * `pkg.MyClass$$ExternalSyntheticLambda0`.
*/
inline fun namedRunnable(tag: String, crossinline block: () -> Unit): Runnable {
return object : Runnable, TraceNameSupplier {
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
index 5d8029293107..db4ab7edbcf1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
@@ -19,7 +19,7 @@ package com.android.systemui.util.animation
import android.animation.ValueAnimator
import android.graphics.PointF
import android.util.MathUtils
-import com.android.systemui.animation.Interpolators
+import com.android.app.animation.Interpolators
/**
* The fraction after which we start fading in when going from a gone widget to a visible one
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 77210b78832b..91078dc65477 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -109,6 +109,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -119,7 +120,6 @@ import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
import com.android.systemui.R;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualLocationsService.kt b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualLocationsService.kt
new file mode 100644
index 000000000000..1c17fc34a34a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualLocationsService.kt
@@ -0,0 +1,93 @@
+package com.android.systemui.wallet.controller
+
+import android.content.Intent
+import android.os.IBinder
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.LifecycleService
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Serves as an intermediary between QuickAccessWalletService and ContextualCardManager (in PCC).
+ * When QuickAccessWalletService has a list of store locations, WalletContextualLocationsService
+ * will send them to ContextualCardManager. When the user enters a store location, this Service
+ * class will be notified, and WalletContextualSuggestionsController will be updated.
+ */
+class WalletContextualLocationsService
+@Inject
+constructor(
+ private val controller: WalletContextualSuggestionsController,
+ private val featureFlags: FeatureFlags,
+) : LifecycleService() {
+ private var listener: IWalletCardsUpdatedListener? = null
+ private var scope: CoroutineScope = this.lifecycleScope
+
+ @VisibleForTesting
+ constructor(
+ controller: WalletContextualSuggestionsController,
+ featureFlags: FeatureFlags,
+ scope: CoroutineScope,
+ ) : this(controller, featureFlags) {
+ this.scope = scope
+ }
+
+ override fun onBind(intent: Intent): IBinder {
+ super.onBind(intent)
+ scope.launch {
+ controller.allWalletCards.collect { cards ->
+ val cardsSize = cards.size
+ Log.i(TAG, "Number of cards registered $cardsSize")
+ listener?.registerNewWalletCards(cards)
+ }
+ }
+ return binder
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ listener = null
+ }
+
+ @VisibleForTesting
+ fun addWalletCardsUpdatedListenerInternal(listener: IWalletCardsUpdatedListener) {
+ if (!featureFlags.isEnabled(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS)) {
+ return
+ }
+ this.listener = listener // Currently, only one listener at a time is supported
+ // Sends WalletCard objects from QuickAccessWalletService to the listener
+ val cards = controller.allWalletCards.value
+ if (!cards.isEmpty()) {
+ val cardsSize = cards.size
+ Log.i(TAG, "Number of cards registered $cardsSize")
+ listener.registerNewWalletCards(cards)
+ }
+ }
+
+ @VisibleForTesting
+ fun onWalletContextualLocationsStateUpdatedInternal(storeLocations: List<String>) {
+ if (!featureFlags.isEnabled(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS)) {
+ return
+ }
+ Log.i(TAG, "Entered store $storeLocations")
+ controller.setSuggestionCardIds(storeLocations.toSet())
+ }
+
+ private val binder: IWalletContextualLocationsService.Stub
+ = object : IWalletContextualLocationsService.Stub() {
+ override fun addWalletCardsUpdatedListener(listener: IWalletCardsUpdatedListener) {
+ addWalletCardsUpdatedListenerInternal(listener)
+ }
+ override fun onWalletContextualLocationsStateUpdated(storeLocations: List<String>) {
+ onWalletContextualLocationsStateUpdatedInternal(storeLocations)
+ }
+ }
+
+ companion object {
+ private const val TAG = "WalletContextualLocationsService"
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
index 518f5a774d7f..b3ad9b0c6a37 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
@@ -36,6 +36,7 @@ import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow
@@ -57,7 +58,8 @@ constructor(
) {
private val cardsReceivedCallbacks: MutableSet<(List<WalletCard>) -> Unit> = mutableSetOf()
- private val allWalletCards: Flow<List<WalletCard>> =
+ /** All potential cards. */
+ val allWalletCards: StateFlow<List<WalletCard>> =
if (featureFlags.isEnabled(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS)) {
// TODO(b/237409756) determine if we should debounce this so we don't call the service
// too frequently. Also check if the list actually changed before calling callbacks.
@@ -107,12 +109,13 @@ constructor(
emptyList()
)
} else {
- emptyFlow()
+ MutableStateFlow<List<WalletCard>>(emptyList()).asStateFlow()
}
private val _suggestionCardIds: MutableStateFlow<Set<String>> = MutableStateFlow(emptySet())
private val contextualSuggestionsCardIds: Flow<Set<String>> = _suggestionCardIds.asStateFlow()
+ /** Contextually-relevant cards. */
val contextualSuggestionCards: Flow<List<WalletCard>> =
combine(allWalletCards, contextualSuggestionsCardIds) { cards, ids ->
val ret =
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
index 9429d8991090..efba3e5d9c34 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
@@ -35,6 +35,8 @@ import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey;
+import android.app.Service;
+import com.android.systemui.wallet.controller.WalletContextualLocationsService;
/**
* Module for injecting classes in Wallet.
@@ -42,6 +44,12 @@ import dagger.multibindings.StringKey;
@Module
public abstract class WalletModule {
+ @Binds
+ @IntoMap
+ @ClassKey(WalletContextualLocationsService.class)
+ abstract Service bindWalletContextualLocationsService(
+ WalletContextualLocationsService service);
+
/** */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index a4b093d1832a..a5365fbc3d5d 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -66,7 +66,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Co
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -100,7 +100,7 @@ public class BubblesManager {
private final INotificationManager mNotificationManager;
private final IDreamManager mDreamManager;
private final NotificationVisibilityProvider mVisibilityProvider;
- private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+ private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider;
private final NotificationLockscreenUserManager mNotifUserManager;
private final CommonNotifCollection mCommonNotifCollection;
private final NotifPipeline mNotifPipeline;
@@ -126,7 +126,7 @@ public class BubblesManager {
INotificationManager notificationManager,
IDreamManager dreamManager,
NotificationVisibilityProvider visibilityProvider,
- NotificationInterruptStateProvider interruptionStateProvider,
+ VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
CommonNotifCollection notifCollection,
@@ -145,7 +145,7 @@ public class BubblesManager {
notificationManager,
dreamManager,
visibilityProvider,
- interruptionStateProvider,
+ visualInterruptionDecisionProvider,
zenModeController,
notifUserManager,
notifCollection,
@@ -169,7 +169,7 @@ public class BubblesManager {
INotificationManager notificationManager,
IDreamManager dreamManager,
NotificationVisibilityProvider visibilityProvider,
- NotificationInterruptStateProvider interruptionStateProvider,
+ VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
CommonNotifCollection notifCollection,
@@ -185,7 +185,7 @@ public class BubblesManager {
mNotificationManager = notificationManager;
mDreamManager = dreamManager;
mVisibilityProvider = visibilityProvider;
- mNotificationInterruptStateProvider = interruptionStateProvider;
+ mVisualInterruptionDecisionProvider = visualInterruptionDecisionProvider;
mNotifUserManager = notifUserManager;
mCommonNotifCollection = notifCollection;
mNotifPipeline = notifPipeline;
@@ -272,7 +272,7 @@ public class BubblesManager {
for (NotificationEntry entry : activeEntries) {
if (mNotifUserManager.isCurrentProfile(entry.getSbn().getUserId())
&& savedBubbleKeys.contains(entry.getKey())
- && mNotificationInterruptStateProvider.shouldBubbleUp(entry)
+ && shouldBubbleUp(entry)
&& entry.isBubble()) {
result.add(notifToBubbleEntry(entry));
}
@@ -416,16 +416,13 @@ public class BubblesManager {
}
void onEntryAdded(NotificationEntry entry) {
- if (mNotificationInterruptStateProvider.shouldBubbleUp(entry)
- && entry.isBubble()) {
+ if (shouldBubbleUp(entry) && entry.isBubble()) {
mBubbles.onEntryAdded(notifToBubbleEntry(entry));
}
}
void onEntryUpdated(NotificationEntry entry, boolean fromSystem) {
- boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry);
- mBubbles.onEntryUpdated(notifToBubbleEntry(entry),
- shouldBubble, fromSystem);
+ mBubbles.onEntryUpdated(notifToBubbleEntry(entry), shouldBubbleUp(entry), fromSystem);
}
void onEntryRemoved(NotificationEntry entry) {
@@ -438,12 +435,8 @@ public class BubblesManager {
for (int i = 0; i < orderedKeys.length; i++) {
String key = orderedKeys[i];
final NotificationEntry entry = mCommonNotifCollection.getEntry(key);
- BubbleEntry bubbleEntry = entry != null
- ? notifToBubbleEntry(entry)
- : null;
- boolean shouldBubbleUp = entry != null
- ? mNotificationInterruptStateProvider.shouldBubbleUp(entry)
- : false;
+ BubbleEntry bubbleEntry = entry != null ? notifToBubbleEntry(entry) : null;
+ boolean shouldBubbleUp = entry != null ? shouldBubbleUp(entry) : false;
pendingOrActiveNotif.put(key, new Pair<>(bubbleEntry, shouldBubbleUp));
}
mBubbles.onRankingUpdated(rankingMap, pendingOrActiveNotif);
@@ -637,6 +630,10 @@ public class BubblesManager {
}
}
+ private boolean shouldBubbleUp(NotificationEntry e) {
+ return mVisualInterruptionDecisionProvider.makeAndLogBubbleDecision(e).getShouldInterrupt();
+ }
+
/**
* Callback for when the BubbleController wants to interact with the notification pipeline to:
* - Remove a previously bubbled notification
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index e492534b3ff6..b3e7cb0c77eb 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -345,7 +345,8 @@ public final class WMShell implements
}
void initDesktopMode(DesktopMode desktopMode) {
- desktopMode.addListener(new DesktopModeTaskRepository.VisibleTasksListener() {
+ desktopMode.addVisibleTasksListener(
+ new DesktopModeTaskRepository.VisibleTasksListener() {
@Override
public void onVisibilityChanged(boolean hasFreeformTasks) {
mSysUiState.setFlag(SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE, hasFreeformTasks)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
index 8a5c5b58d058..57a355f4e127 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
@@ -106,4 +106,29 @@ class FontInterpolatorTest : SysuiTestCase() {
val reversedFont = interp.lerp(endFont, startFont, 0.5f)
assertThat(resultFont).isSameInstanceAs(reversedFont)
}
+
+ @Test
+ fun testCacheMaxSize() {
+ val interp = FontInterpolator()
+
+ val startFont = Font.Builder(sFont)
+ .setFontVariationSettings("'wght' 100")
+ .build()
+ val endFont = Font.Builder(sFont)
+ .setFontVariationSettings("'wght' 1")
+ .build()
+ val resultFont = interp.lerp(startFont, endFont, 0.5f)
+ for (i in 0..FONT_CACHE_MAX_ENTRIES + 1) {
+ val f1 = Font.Builder(sFont)
+ .setFontVariationSettings("'wght' ${i * 100}")
+ .build()
+ val f2 = Font.Builder(sFont)
+ .setFontVariationSettings("'wght' $i")
+ .build()
+ interp.lerp(f1, f2, 0.5f)
+ }
+
+ val cachedFont = interp.lerp(startFont, endFont, 0.5f)
+ assertThat(resultFont).isNotSameInstanceAs(cachedFont)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/InterpolatorsAndroidXTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/InterpolatorsAndroidXTest.kt
deleted file mode 100644
index 2c680be97e95..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/InterpolatorsAndroidXTest.kt
+++ /dev/null
@@ -1,55 +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.animation
-
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import java.lang.reflect.Modifier
-import junit.framework.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@SmallTest
-@RunWith(JUnit4::class)
-class InterpolatorsAndroidXTest : SysuiTestCase() {
-
- @Test
- fun testInterpolatorsAndInterpolatorsAndroidXPublicMethodsAreEqual() {
- assertEquals(
- Interpolators::class.java.getPublicMethods(),
- InterpolatorsAndroidX::class.java.getPublicMethods()
- )
- }
-
- @Test
- fun testInterpolatorsAndInterpolatorsAndroidXPublicFieldsAreEqual() {
- assertEquals(
- Interpolators::class.java.getPublicFields(),
- InterpolatorsAndroidX::class.java.getPublicFields()
- )
- }
-
- private fun <T> Class<T>.getPublicMethods() =
- declaredMethods
- .filter { Modifier.isPublic(it.modifiers) }
- .map { it.toString().replace(name, "") }
- .toSet()
-
- private fun <T> Class<T>.getPublicFields() =
- fields.filter { Modifier.isPublic(it.modifiers) }.map { it.name }.toSet()
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
index 02d4ecd665fc..063757acc1a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
@@ -31,6 +31,7 @@ import android.text.TextPaint
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.io.File
@@ -64,6 +65,7 @@ private val END_PAINT = TextPaint(PAINT).apply {
@RunWith(AndroidTestingRunner::class)
@SmallTest
class TextInterpolatorTest : SysuiTestCase() {
+ lateinit var typefaceCache: TypefaceVariantCache
private fun makeLayout(
text: String,
@@ -75,11 +77,16 @@ class TextInterpolatorTest : SysuiTestCase() {
.setTextDirection(dir).build()
}
+ @Before
+ fun setup() {
+ typefaceCache = TypefaceVariantCacheImpl()
+ }
+
@Test
fun testStartState() {
val layout = makeLayout(TEXT, PAINT)
- val interp = TextInterpolator(layout)
+ val interp = TextInterpolator(layout, typefaceCache)
interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
@@ -98,7 +105,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testEndState() {
val layout = makeLayout(TEXT, PAINT)
- val interp = TextInterpolator(layout)
+ val interp = TextInterpolator(layout, typefaceCache)
interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
@@ -116,7 +123,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testMiddleState() {
val layout = makeLayout(TEXT, PAINT)
- val interp = TextInterpolator(layout)
+ val interp = TextInterpolator(layout, typefaceCache)
interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
@@ -138,7 +145,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testRebase() {
val layout = makeLayout(TEXT, PAINT)
- val interp = TextInterpolator(layout)
+ val interp = TextInterpolator(layout, typefaceCache)
interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
@@ -160,7 +167,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testBidi_LTR() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.LTR)
- val interp = TextInterpolator(layout)
+ val interp = TextInterpolator(layout, typefaceCache)
interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
@@ -180,7 +187,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testBidi_RTL() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
- val interp = TextInterpolator(layout)
+ val interp = TextInterpolator(layout, typefaceCache)
interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
@@ -200,7 +207,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testGlyphCallback_Empty() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
- val interp = TextInterpolator(layout).apply {
+ val interp = TextInterpolator(layout, typefaceCache).apply {
glyphFilter = { glyph, progress ->
}
}
@@ -222,7 +229,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testGlyphCallback_Xcoordinate() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
- val interp = TextInterpolator(layout).apply {
+ val interp = TextInterpolator(layout, typefaceCache).apply {
glyphFilter = { glyph, progress ->
glyph.x += 30f
}
@@ -247,7 +254,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testGlyphCallback_Ycoordinate() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
- val interp = TextInterpolator(layout).apply {
+ val interp = TextInterpolator(layout, typefaceCache).apply {
glyphFilter = { glyph, progress ->
glyph.y += 30f
}
@@ -272,7 +279,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testGlyphCallback_TextSize() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
- val interp = TextInterpolator(layout).apply {
+ val interp = TextInterpolator(layout, typefaceCache).apply {
glyphFilter = { glyph, progress ->
glyph.textSize += 10f
}
@@ -297,7 +304,7 @@ class TextInterpolatorTest : SysuiTestCase() {
fun testGlyphCallback_Color() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
- val interp = TextInterpolator(layout).apply {
+ val interp = TextInterpolator(layout, typefaceCache).apply {
glyphFilter = { glyph, progress ->
glyph.color = Color.RED
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
index 6ab54a374d30..da9ceb47446a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
@@ -19,6 +19,7 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import com.android.app.animation.Interpolators
@SmallTest
@RunWith(AndroidTestingRunner::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 7d9ccb642e47..6b5679a2a92a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -21,6 +21,7 @@ import android.hardware.biometrics.BiometricSourceType
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import android.util.DisplayMetrics
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.keyguard.KeyguardUpdateMonitor
@@ -35,7 +36,6 @@ 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.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.leak.RotationUtils
@@ -66,7 +66,6 @@ class AuthRippleControllerTest : SysuiTestCase() {
private lateinit var staticMockSession: MockitoSession
private lateinit var controller: AuthRippleController
- @Mock private lateinit var mCentralSurfaces: CentralSurfaces
@Mock private lateinit var rippleView: AuthRippleView
@Mock private lateinit var commandRegistry: CommandRegistry
@Mock private lateinit var configurationController: ConfigurationController
@@ -92,6 +91,8 @@ class AuthRippleControllerTest : SysuiTestCase() {
@Mock
private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
+ private val displayMetrics = DisplayMetrics()
+
@Captor
private lateinit var biometricUnlockListener:
ArgumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener>
@@ -109,7 +110,6 @@ class AuthRippleControllerTest : SysuiTestCase() {
`when`(udfpsControllerProvider.get()).thenReturn(udfpsController)
controller = AuthRippleController(
- mCentralSurfaces,
context,
authController,
configurationController,
@@ -120,13 +120,14 @@ class AuthRippleControllerTest : SysuiTestCase() {
notificationShadeWindowController,
udfpsControllerProvider,
statusBarStateController,
+ displayMetrics,
featureFlags,
KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)),
biometricUnlockController,
+ lightRevealScrim,
rippleView,
)
controller.init()
- `when`(mCentralSurfaces.lightRevealScrim).thenReturn(lightRevealScrim)
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java
new file mode 100644
index 000000000000..362d26b040e8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.concurrent.ExecutionException;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class BiometricNotificationDialogFactoryTest extends SysuiTestCase {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Mock
+ FingerprintManager mFingerprintManager;
+ @Mock
+ FaceManager mFaceManager;
+ @Mock
+ SystemUIDialog mDialog;
+
+ private Context mContextSpy;
+ private final ArgumentCaptor<DialogInterface.OnClickListener> mOnClickListenerArgumentCaptor =
+ ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
+ private final ArgumentCaptor<Intent> mIntentArgumentCaptor =
+ ArgumentCaptor.forClass(Intent.class);
+ private BiometricNotificationDialogFactory mDialogFactory;
+
+ @Before
+ public void setUp() throws ExecutionException, InterruptedException {
+ mContext.addMockSystemService(FingerprintManager.class, mFingerprintManager);
+ mContext.addMockSystemService(FaceManager.class, mFaceManager);
+
+ when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+
+ mContextSpy = spy(mContext);
+ mDialogFactory = new BiometricNotificationDialogFactory();
+ }
+
+ @Test
+ public void testFingerprintReEnrollDialog_onRemovalSucceeded() {
+ mDialogFactory.createReenrollDialog(mContextSpy, mDialog,
+ BiometricSourceType.FINGERPRINT);
+
+ verify(mDialog).setPositiveButton(anyInt(), mOnClickListenerArgumentCaptor.capture());
+
+ DialogInterface.OnClickListener positiveOnClickListener =
+ mOnClickListenerArgumentCaptor.getValue();
+ positiveOnClickListener.onClick(null, DialogInterface.BUTTON_POSITIVE);
+ ArgumentCaptor<FingerprintManager.RemovalCallback> removalCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(FingerprintManager.RemovalCallback.class);
+
+ verify(mFingerprintManager).removeAll(anyInt(), removalCallbackArgumentCaptor.capture());
+
+ removalCallbackArgumentCaptor.getValue().onRemovalSucceeded(null /* fp */,
+ 0 /* remaining */);
+
+ verify(mContextSpy).startActivity(mIntentArgumentCaptor.capture());
+ assertThat(mIntentArgumentCaptor.getValue().getAction()).isEqualTo(
+ Settings.ACTION_FINGERPRINT_ENROLL);
+ }
+
+ @Test
+ public void testFingerprintReEnrollDialog_onRemovalError() {
+ mDialogFactory.createReenrollDialog(mContextSpy, mDialog,
+ BiometricSourceType.FINGERPRINT);
+
+ verify(mDialog).setPositiveButton(anyInt(), mOnClickListenerArgumentCaptor.capture());
+
+ DialogInterface.OnClickListener positiveOnClickListener =
+ mOnClickListenerArgumentCaptor.getValue();
+ positiveOnClickListener.onClick(null, DialogInterface.BUTTON_POSITIVE);
+ ArgumentCaptor<FingerprintManager.RemovalCallback> removalCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(FingerprintManager.RemovalCallback.class);
+
+ verify(mFingerprintManager).removeAll(anyInt(), removalCallbackArgumentCaptor.capture());
+
+ removalCallbackArgumentCaptor.getValue().onRemovalError(null /* fp */,
+ 0 /* errmsgId */, "Error" /* errString */);
+
+ verify(mContextSpy, never()).startActivity(any());
+ }
+
+ @Test
+ public void testFaceReEnrollDialog_onRemovalSucceeded() {
+ mDialogFactory.createReenrollDialog(mContextSpy, mDialog,
+ BiometricSourceType.FACE);
+
+ verify(mDialog).setPositiveButton(anyInt(), mOnClickListenerArgumentCaptor.capture());
+
+ DialogInterface.OnClickListener positiveOnClickListener =
+ mOnClickListenerArgumentCaptor.getValue();
+ positiveOnClickListener.onClick(null, DialogInterface.BUTTON_POSITIVE);
+ ArgumentCaptor<FaceManager.RemovalCallback> removalCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(FaceManager.RemovalCallback.class);
+
+ verify(mFaceManager).removeAll(anyInt(), removalCallbackArgumentCaptor.capture());
+
+ removalCallbackArgumentCaptor.getValue().onRemovalSucceeded(null /* fp */,
+ 0 /* remaining */);
+
+ verify(mContextSpy).startActivity(mIntentArgumentCaptor.capture());
+ assertThat(mIntentArgumentCaptor.getValue().getAction()).isEqualTo(
+ "android.settings.FACE_ENROLL");
+ }
+
+ @Test
+ public void testFaceReEnrollDialog_onRemovalError() {
+ mDialogFactory.createReenrollDialog(mContextSpy, mDialog,
+ BiometricSourceType.FACE);
+
+ verify(mDialog).setPositiveButton(anyInt(), mOnClickListenerArgumentCaptor.capture());
+
+ DialogInterface.OnClickListener positiveOnClickListener =
+ mOnClickListenerArgumentCaptor.getValue();
+ positiveOnClickListener.onClick(null, DialogInterface.BUTTON_POSITIVE);
+ ArgumentCaptor<FaceManager.RemovalCallback> removalCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(FaceManager.RemovalCallback.class);
+
+ verify(mFaceManager).removeAll(anyInt(), removalCallbackArgumentCaptor.capture());
+
+ removalCallbackArgumentCaptor.getValue().onRemovalError(null /* face */,
+ 0 /* errmsgId */, "Error" /* errString */);
+
+ verify(mContextSpy, never()).startActivity(any());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
new file mode 100644
index 000000000000..b8bca3a403e1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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 static com.android.systemui.biometrics.BiometricNotificationBroadcastReceiver.ACTION_SHOW_FACE_REENROLL_DIALOG;
+import static com.android.systemui.biometrics.BiometricNotificationBroadcastReceiver.ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricSourceType;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class BiometricNotificationServiceTest extends SysuiTestCase {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ @Mock
+ KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ KeyguardStateController mKeyguardStateController;
+ @Mock
+ NotificationManager mNotificationManager;
+
+ private static final String TAG = "BiometricNotificationService";
+ private static final int FACE_NOTIFICATION_ID = 1;
+ private static final int FINGERPRINT_NOTIFICATION_ID = 2;
+ private static final long SHOW_NOTIFICATION_DELAY_MS = 5_000L; // 5 seconds
+
+ private final ArgumentCaptor<Notification> mNotificationArgumentCaptor =
+ ArgumentCaptor.forClass(Notification.class);
+ private TestableLooper mLooper;
+ private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback;
+ private KeyguardStateController.Callback mKeyguardStateControllerCallback;
+
+ @Before
+ public void setUp() {
+ mLooper = TestableLooper.get(this);
+ Handler handler = new Handler(mLooper.getLooper());
+ BiometricNotificationDialogFactory dialogFactory = new BiometricNotificationDialogFactory();
+ BiometricNotificationBroadcastReceiver broadcastReceiver =
+ new BiometricNotificationBroadcastReceiver(mContext, dialogFactory);
+ BiometricNotificationService biometricNotificationService =
+ new BiometricNotificationService(mContext,
+ mKeyguardUpdateMonitor, mKeyguardStateController, handler,
+ mNotificationManager,
+ broadcastReceiver);
+ biometricNotificationService.start();
+
+ ArgumentCaptor<KeyguardUpdateMonitorCallback> updateMonitorCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+ ArgumentCaptor<KeyguardStateController.Callback> stateControllerCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
+
+ verify(mKeyguardUpdateMonitor).registerCallback(
+ updateMonitorCallbackArgumentCaptor.capture());
+ verify(mKeyguardStateController).addCallback(
+ stateControllerCallbackArgumentCaptor.capture());
+
+ mKeyguardUpdateMonitorCallback = updateMonitorCallbackArgumentCaptor.getValue();
+ mKeyguardStateControllerCallback = stateControllerCallbackArgumentCaptor.getValue();
+ }
+
+ @Test
+ public void testShowFingerprintReEnrollNotification() {
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+ mKeyguardUpdateMonitorCallback.onBiometricError(
+ BiometricFingerprintConstants.BIOMETRIC_ERROR_RE_ENROLL,
+ "Testing Fingerprint Re-enrollment" /* errString */,
+ BiometricSourceType.FINGERPRINT
+ );
+ mKeyguardStateControllerCallback.onKeyguardShowingChanged();
+
+ mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS);
+ mLooper.processAllMessages();
+
+ verify(mNotificationManager).notifyAsUser(eq(TAG), eq(FINGERPRINT_NOTIFICATION_ID),
+ mNotificationArgumentCaptor.capture(), any());
+
+ Notification fingerprintNotification = mNotificationArgumentCaptor.getValue();
+
+ assertThat(fingerprintNotification.contentIntent.getIntent().getAction())
+ .isEqualTo(ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG);
+ }
+ @Test
+ public void testShowFaceReEnrollNotification() {
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+ mKeyguardUpdateMonitorCallback.onBiometricError(
+ BiometricFaceConstants.BIOMETRIC_ERROR_RE_ENROLL,
+ "Testing Face Re-enrollment" /* errString */,
+ BiometricSourceType.FACE
+ );
+ mKeyguardStateControllerCallback.onKeyguardShowingChanged();
+
+ mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS);
+ mLooper.processAllMessages();
+
+ verify(mNotificationManager).notifyAsUser(eq(TAG), eq(FACE_NOTIFICATION_ID),
+ mNotificationArgumentCaptor.capture(), any());
+
+ Notification fingerprintNotification = mNotificationArgumentCaptor.getValue();
+
+ assertThat(fingerprintNotification.contentIntent.getIntent().getAction())
+ .isEqualTo(ACTION_SHOW_FACE_REENROLL_DIALOG);
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
index 58eb7d4f3ea7..e1c54976d734 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/conditions/DreamConditionTest.java
@@ -18,7 +18,6 @@ package com.android.systemui.dreams.conditions;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
@@ -26,13 +25,13 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.DreamManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.condition.Condition;
@@ -55,6 +54,9 @@ public class DreamConditionTest extends SysuiTestCase {
@Mock
DreamManager mDreamManager;
+ @Mock
+ KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -66,7 +68,7 @@ public class DreamConditionTest extends SysuiTestCase {
@Test
public void testInitialDreamingState() {
when(mDreamManager.isDreaming()).thenReturn(true);
- final DreamCondition condition = new DreamCondition(mContext, mDreamManager);
+ final DreamCondition condition = new DreamCondition(mDreamManager, mKeyguardUpdateMonitor);
condition.addCallback(mCallback);
verify(mCallback).onConditionChanged(eq(condition));
@@ -79,7 +81,7 @@ public class DreamConditionTest extends SysuiTestCase {
@Test
public void testInitialNonDreamingState() {
when(mDreamManager.isDreaming()).thenReturn(false);
- final DreamCondition condition = new DreamCondition(mContext, mDreamManager);
+ final DreamCondition condition = new DreamCondition(mDreamManager, mKeyguardUpdateMonitor);
condition.addCallback(mCallback);
verify(mCallback, never()).onConditionChanged(eq(condition));
@@ -91,15 +93,21 @@ public class DreamConditionTest extends SysuiTestCase {
*/
@Test
public void testChange() {
- final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
+ final ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
when(mDreamManager.isDreaming()).thenReturn(true);
- final DreamCondition condition = new DreamCondition(mContext, mDreamManager);
+ final DreamCondition condition = new DreamCondition(mDreamManager, mKeyguardUpdateMonitor);
condition.addCallback(mCallback);
- verify(mContext).registerReceiver(receiverCaptor.capture(), any());
+ verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture());
+
clearInvocations(mCallback);
- receiverCaptor.getValue().onReceive(mContext, new Intent(Intent.ACTION_DREAMING_STOPPED));
+ callbackCaptor.getValue().onDreamingStateChanged(false);
verify(mCallback).onConditionChanged(eq(condition));
assertThat(condition.isConditionMet()).isFalse();
+
+ clearInvocations(mCallback);
+ callbackCaptor.getValue().onDreamingStateChanged(true);
+ verify(mCallback).onConditionChanged(eq(condition));
+ assertThat(condition.isConditionMet()).isTrue();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
index f6ff4b214035..6f9dedf9dda8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
@@ -96,6 +96,16 @@ class KeyboardRepositoryTest : SysuiTestCase() {
}
@Test
+ fun emitsDisconnected_whenDeviceWithIdDoesNotExist() =
+ testScope.runTest {
+ val deviceListener = captureDeviceListener()
+ val isKeyboardConnected by collectLastValue(underTest.keyboardConnected)
+
+ deviceListener.onInputDeviceAdded(NULL_DEVICE_ID)
+ assertThat(isKeyboardConnected).isFalse()
+ }
+
+ @Test
fun emitsDisconnected_whenKeyboardDisconnects() =
testScope.runTest {
val deviceListener = captureDeviceListener()
@@ -172,6 +182,7 @@ class KeyboardRepositoryTest : SysuiTestCase() {
private const val VIRTUAL_FULL_KEYBOARD_ID = 2
private const val PHYSICAL_NOT_FULL_KEYBOARD_ID = 3
private const val ANOTHER_PHYSICAL_FULL_KEYBOARD_ID = 4
+ private const val NULL_DEVICE_ID = 5
private val INPUT_DEVICES_MAP: Map<Int, InputDevice> =
mapOf(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index d0bfaa9bedc9..5afc4059357c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -23,8 +23,8 @@ import android.util.Log.TerribleFailureHandler
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
+import com.android.app.animation.Interpolators
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.Interpolators
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 96fff6499be5..23f05233b5e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -635,14 +635,14 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
}
companion object {
- private val ICON: Icon = mock {
- whenever(this.contentDescription)
- .thenReturn(
+ private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
+ private val ICON: Icon =
+ Icon.Resource(
+ res = CONTENT_DESCRIPTION_RESOURCE_ID,
+ contentDescription =
ContentDescription.Resource(
res = CONTENT_DESCRIPTION_RESOURCE_ID,
- )
- )
- }
- private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
+ ),
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
index a5b78b74fcdf..3efe38295f3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.keyguard.ui
import androidx.test.filters.SmallTest
+import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index a72634bcb807..1a00ac2722fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -110,6 +110,7 @@ class MediaCarouselControllerTest : SysuiTestCase() {
lateinit var configListener: ArgumentCaptor<ConfigurationController.ConfigurationListener>
@Captor lateinit var visualStabilityCallback: ArgumentCaptor<OnReorderingAllowedListener>
@Captor lateinit var keyguardCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
+ @Captor lateinit var hostStateCallback: ArgumentCaptor<MediaHostStatesManager.Callback>
private val clock = FakeSystemClock()
private lateinit var mediaCarouselController: MediaCarouselController
@@ -143,6 +144,7 @@ class MediaCarouselControllerTest : SysuiTestCase() {
verify(visualStabilityProvider)
.addPersistentReorderingAllowedListener(capture(visualStabilityCallback))
verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCallback))
+ verify(mediaHostStatesManager).addCallback(capture(hostStateCallback))
whenever(mediaControlPanelFactory.get()).thenReturn(panel)
whenever(panel.mediaViewController).thenReturn(mediaViewController)
whenever(mediaDataManager.smartspaceMediaData).thenReturn(smartspaceMediaData)
@@ -832,4 +834,16 @@ class MediaCarouselControllerTest : SysuiTestCase() {
// Verify that seekbar listening attribute in media control panel is set to false.
verify(panel, times(MediaPlayerData.players().size)).listening = false
}
+
+ @Test
+ fun testOnHostStateChanged_updateVisibility() {
+ var stateUpdated = false
+ mediaCarouselController.updateUserVisibility = { stateUpdated = true }
+
+ // When the host state updates
+ hostStateCallback.value!!.onHostStateChanged(LOCATION_QS, mediaHostState)
+
+ // Then the carousel visibility is updated
+ assertTrue(stateUpdated)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
index eb78ded008b2..2ce236d4ba89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
@@ -470,6 +470,21 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
)
}
+ @Test
+ fun testQsExpandedChanged_noQqsMedia() {
+ // When we are looking at QQS with active media
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+ whenever(statusBarStateController.isExpanded).thenReturn(true)
+
+ // When there is no longer any active media
+ whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(false)
+ mediaHierarchyManager.qsExpanded = false
+
+ // Then the carousel is set to not visible
+ verify(mediaCarouselScrollHandler).visibleToUser = false
+ assertThat(mediaCarouselScrollHandler.visibleToUser).isFalse()
+ }
+
private fun enableSplitShade() {
context
.getOrCreateTestableResources()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
index 8e32f81b193f..d9428f897f0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
@@ -124,7 +124,7 @@ class BackPanelControllerTest : SysuiTestCase() {
continueTouch(START_X + touchSlop.toFloat() + 1)
continueTouch(
START_X + touchSlop + triggerThreshold -
- mBackPanelController.params.deactivationSwipeTriggerThreshold
+ mBackPanelController.params.deactivationTriggerThreshold
)
clearInvocations(backCallback)
Thread.sleep(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index 7dc622b86b4e..55f221df1f0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -620,6 +620,38 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
}
}
+ // region onRoleHoldersChanged
+ @Test
+ fun onRoleHoldersChanged_notNotesRole_doNothing() {
+ val user = UserHandle.of(0)
+
+ createNoteTaskController(isEnabled = true).onRoleHoldersChanged("NOT_NOTES", user)
+
+ verifyZeroInteractions(context)
+ }
+
+ @Test
+ fun onRoleHoldersChanged_notesRole_sameUser_shouldUpdateShortcuts() {
+ val user = userTracker.userHandle
+ val controller = spy(createNoteTaskController())
+ doNothing().whenever(controller).updateNoteTaskAsUser(any())
+
+ controller.onRoleHoldersChanged(ROLE_NOTES, user)
+
+ verify(controller).updateNoteTaskAsUser(user)
+ }
+
+ @Test
+ fun onRoleHoldersChanged_notesRole_differentUser_shouldUpdateShortcutsInUserProcess() {
+ // FakeUserTracker will default to UserHandle.SYSTEM.
+ val user = UserHandle.CURRENT
+
+ createNoteTaskController(isEnabled = true).onRoleHoldersChanged(ROLE_NOTES, user)
+
+ verify(context).startServiceAsUser(any(), eq(user))
+ }
+ // endregion
+
// region updateNoteTaskAsUser
@Test
fun updateNoteTaskAsUser_withNotesRole_withShortcuts_shouldUpdateShortcuts() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt
index db6fc136e651..38a666eeb410 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt
@@ -37,6 +37,7 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -54,10 +55,12 @@ class NotificationPanelUnfoldAnimationControllerTest : SysuiTestCase() {
@Mock private lateinit var parent: ViewGroup
+ @Mock private lateinit var splitShadeStatusBar: ViewGroup
+
@Mock private lateinit var statusBarStateController: StatusBarStateController
private lateinit var underTest: NotificationPanelUnfoldAnimationController
- private lateinit var progressListener: TransitionProgressListener
+ private lateinit var progressListeners: List<TransitionProgressListener>
private var xTranslationMax = 0f
@Before
@@ -73,10 +76,13 @@ class NotificationPanelUnfoldAnimationControllerTest : SysuiTestCase() {
statusBarStateController,
progressProvider
)
+ whenever(parent.findViewById<ViewGroup>(R.id.split_shade_status_bar)).thenReturn(
+ splitShadeStatusBar
+ )
underTest.setup(parent)
- verify(progressProvider).addCallback(capture(progressListenerCaptor))
- progressListener = progressListenerCaptor.value
+ verify(progressProvider, atLeastOnce()).addCallback(capture(progressListenerCaptor))
+ progressListeners = progressListenerCaptor.allValues
}
@Test
@@ -86,16 +92,16 @@ class NotificationPanelUnfoldAnimationControllerTest : SysuiTestCase() {
val view = View(context)
whenever(parent.findViewById<View>(R.id.quick_settings_panel)).thenReturn(view)
- progressListener.onTransitionStarted()
+ onTransitionStarted()
assertThat(view.translationX).isZero()
- progressListener.onTransitionProgress(0f)
+ onTransitionProgress(0f)
assertThat(view.translationX).isZero()
- progressListener.onTransitionProgress(0.5f)
+ onTransitionProgress(0.5f)
assertThat(view.translationX).isZero()
- progressListener.onTransitionFinished()
+ onTransitionFinished()
assertThat(view.translationX).isZero()
}
@@ -106,16 +112,16 @@ class NotificationPanelUnfoldAnimationControllerTest : SysuiTestCase() {
val view = View(context)
whenever(parent.findViewById<View>(R.id.quick_settings_panel)).thenReturn(view)
- progressListener.onTransitionStarted()
+ onTransitionStarted()
assertThat(view.translationX).isZero()
- progressListener.onTransitionProgress(0f)
+ onTransitionProgress(0f)
assertThat(view.translationX).isEqualTo(xTranslationMax)
- progressListener.onTransitionProgress(0.5f)
+ onTransitionProgress(0.5f)
assertThat(view.translationX).isEqualTo(0.5f * xTranslationMax)
- progressListener.onTransitionFinished()
+ onTransitionFinished()
assertThat(view.translationX).isZero()
}
@@ -126,16 +132,88 @@ class NotificationPanelUnfoldAnimationControllerTest : SysuiTestCase() {
val view = View(context)
whenever(parent.findViewById<View>(R.id.quick_settings_panel)).thenReturn(view)
- progressListener.onTransitionStarted()
+ onTransitionStarted()
assertThat(view.translationX).isZero()
- progressListener.onTransitionProgress(0f)
+ onTransitionProgress(0f)
assertThat(view.translationX).isEqualTo(xTranslationMax)
- progressListener.onTransitionProgress(0.5f)
+ onTransitionProgress(0.5f)
assertThat(view.translationX).isEqualTo(0.5f * xTranslationMax)
- progressListener.onTransitionFinished()
+ onTransitionFinished()
assertThat(view.translationX).isZero()
}
+
+ @Test
+ fun whenInKeyguardState_statusBarViewDoesNotMove() {
+ whenever(statusBarStateController.getState()).thenReturn(KEYGUARD)
+
+ val view = View(context)
+ whenever(splitShadeStatusBar.findViewById<View>(R.id.date)).thenReturn(view)
+
+ onTransitionStarted()
+ assertThat(view.translationX).isZero()
+
+ onTransitionProgress(0f)
+ assertThat(view.translationX).isZero()
+
+ onTransitionProgress(0.5f)
+ assertThat(view.translationX).isZero()
+
+ onTransitionFinished()
+ assertThat(view.translationX).isZero()
+ }
+
+ @Test
+ fun whenInShadeState_statusBarViewDoesMove() {
+ whenever(statusBarStateController.getState()).thenReturn(SHADE)
+
+ val view = View(context)
+ whenever(splitShadeStatusBar.findViewById<View>(R.id.date)).thenReturn(view)
+
+ onTransitionStarted()
+ assertThat(view.translationX).isZero()
+
+ onTransitionProgress(0f)
+ assertThat(view.translationX).isEqualTo(xTranslationMax)
+
+ onTransitionProgress(0.5f)
+ assertThat(view.translationX).isEqualTo(0.5f * xTranslationMax)
+
+ onTransitionFinished()
+ assertThat(view.translationX).isZero()
+ }
+
+ @Test
+ fun whenInShadeLockedState_statusBarViewDoesMove() {
+ whenever(statusBarStateController.getState()).thenReturn(SHADE_LOCKED)
+
+ val view = View(context)
+ whenever(splitShadeStatusBar.findViewById<View>(R.id.date)).thenReturn(view)
+ onTransitionStarted()
+ assertThat(view.translationX).isZero()
+
+ onTransitionProgress(0f)
+ assertThat(view.translationX).isEqualTo(xTranslationMax)
+
+ onTransitionProgress(0.5f)
+ assertThat(view.translationX).isEqualTo(0.5f * xTranslationMax)
+
+ onTransitionFinished()
+ assertThat(view.translationX).isZero()
+ }
+
+ private fun onTransitionStarted() {
+ progressListeners.forEach { it.onTransitionStarted() }
+ }
+
+ private fun onTransitionProgress(progress: Float) {
+ progressListeners.forEach { it.onTransitionProgress(progress) }
+ }
+
+ private fun onTransitionFinished() {
+ progressListeners.forEach { it.onTransitionFinished() }
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index 9fe75abddf9c..20da8a619100 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -33,9 +33,9 @@ import android.widget.TextView
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
+import com.android.app.animation.Interpolators
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
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 a1168f809971..f0abf2f4a24f 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
@@ -32,9 +32,9 @@ import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
+import com.android.app.animation.Interpolators;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.notification.stack.AnimationFilter;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ViewState;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index 67128ff5624a..283efe263f04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -38,8 +38,10 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.DecisionImpl
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper.FullScreenIntentDecisionImpl
+import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider
import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback
import com.android.systemui.statusbar.phone.NotificationGroupTestHelper
import com.android.systemui.statusbar.policy.HeadsUpManager
@@ -52,6 +54,7 @@ import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.time.FakeSystemClock
import java.util.ArrayList
import java.util.function.Consumer
+import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -86,7 +89,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
private val logger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true)
private val headsUpManager: HeadsUpManager = mock()
private val headsUpViewBinder: HeadsUpViewBinder = mock()
- private val notificationInterruptStateProvider: NotificationInterruptStateProvider = mock()
+ private val visualInterruptionDecisionProvider: VisualInterruptionDecisionProvider = mock()
private val remoteInputManager: NotificationRemoteInputManager = mock()
private val endLifetimeExtension: OnEndLifetimeExtensionCallback = mock()
private val headerController: NodeController = mock()
@@ -114,7 +117,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
systemClock,
headsUpManager,
headsUpViewBinder,
- notificationInterruptStateProvider,
+ visualInterruptionDecisionProvider,
remoteInputManager,
launchFullScreenIntentProvider,
flags,
@@ -168,8 +171,11 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
groupChild2 = helper.createChildNotification(GROUP_ALERT_ALL, 2, "child", 250)
groupChild3 = helper.createChildNotification(GROUP_ALERT_ALL, 3, "child", 150)
+ // Set the default HUN decision
+ setDefaultShouldHeadsUp(false)
+
// Set the default FSI decision
- setShouldFullScreen(any(), FullScreenIntentDecision.NO_FULL_SCREEN_INTENT)
+ setDefaultShouldFullScreen(FullScreenIntentDecision.NO_FULL_SCREEN_INTENT)
}
@Test
@@ -1006,31 +1012,59 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
verify(launchFullScreenIntentProvider, never()).launchFullScreenIntent(entry)
}
+ private fun setDefaultShouldHeadsUp(should: Boolean) {
+ whenever(visualInterruptionDecisionProvider.makeAndLogHeadsUpDecision(any()))
+ .thenReturn(DecisionImpl.of(should))
+ whenever(visualInterruptionDecisionProvider.makeUnloggedHeadsUpDecision(any()))
+ .thenReturn(DecisionImpl.of(should))
+ }
+
private fun setShouldHeadsUp(entry: NotificationEntry, should: Boolean = true) {
- whenever(notificationInterruptStateProvider.shouldHeadsUp(entry)).thenReturn(should)
- whenever(notificationInterruptStateProvider.checkHeadsUp(eq(entry), any()))
- .thenReturn(should)
+ whenever(visualInterruptionDecisionProvider.makeAndLogHeadsUpDecision(entry))
+ .thenReturn(DecisionImpl.of(should))
+ whenever(visualInterruptionDecisionProvider.makeUnloggedHeadsUpDecision(entry))
+ .thenReturn(DecisionImpl.of(should))
}
- private fun setShouldFullScreen(entry: NotificationEntry, decision: FullScreenIntentDecision) {
- whenever(notificationInterruptStateProvider.getFullScreenIntentDecision(entry))
- .thenReturn(decision)
+ private fun setDefaultShouldFullScreen(
+ originalDecision: FullScreenIntentDecision
+ ) {
+ val provider = visualInterruptionDecisionProvider
+ whenever(provider.makeUnloggedFullScreenIntentDecision(any())).thenAnswer {
+ val entry: NotificationEntry = it.getArgument(0)
+ FullScreenIntentDecisionImpl(entry, originalDecision)
+ }
+ }
+
+ private fun setShouldFullScreen(
+ entry: NotificationEntry,
+ originalDecision: FullScreenIntentDecision
+ ) {
+ whenever(
+ visualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision(entry)
+ ).thenAnswer {
+ FullScreenIntentDecisionImpl(entry, originalDecision)
+ }
}
private fun verifyLoggedFullScreenIntentDecision(
entry: NotificationEntry,
- decision: FullScreenIntentDecision
+ originalDecision: FullScreenIntentDecision
) {
- verify(notificationInterruptStateProvider).logFullScreenIntentDecision(entry, decision)
+ val decision = withArgCaptor {
+ verify(visualInterruptionDecisionProvider).logFullScreenIntentDecision(capture())
+ }
+ check(decision is FullScreenIntentDecisionImpl)
+ assertEquals(entry, decision.originalEntry)
+ assertEquals(originalDecision, decision.originalDecision)
}
private fun verifyNoFullScreenIntentDecisionLogged() {
- verify(notificationInterruptStateProvider, never())
- .logFullScreenIntentDecision(any(), any())
+ verify(visualInterruptionDecisionProvider, never()).logFullScreenIntentDecision(any())
}
private fun clearInterruptionProviderInvocations() {
- clearInvocations(notificationInterruptStateProvider)
+ clearInvocations(visualInterruptionDecisionProvider)
}
private fun finishBind(entry: NotificationEntry) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
index 819a75bffc8e..90cb7341377d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FooterViewTest.java
@@ -24,12 +24,12 @@ import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.mock;
+import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -39,7 +39,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
public class FooterViewTest extends SysuiTestCase {
FooterView mView;
@@ -102,14 +102,21 @@ public class FooterViewTest extends SysuiTestCase {
}
@Test
- public void testSetFooterLabelTextAndIcon() {
- mView.setFooterLabelTextAndIcon(
- R.string.unlock_to_see_notif_text,
- R.drawable.ic_friction_lock_closed);
+ public void testSetFooterLabelVisible() {
+ mView.setFooterLabelVisible(true);
assertThat(mView.findViewById(R.id.manage_text).getVisibility()).isEqualTo(View.GONE);
assertThat(mView.findSecondaryView().getVisibility()).isEqualTo(View.GONE);
assertThat(mView.findViewById(R.id.unlock_prompt_footer).getVisibility())
.isEqualTo(View.VISIBLE);
}
+
+ @Test
+ public void testSetFooterLabelInvisible() {
+ mView.setFooterLabelVisible(false);
+ assertThat(mView.findViewById(R.id.manage_text).getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mView.findSecondaryView().getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mView.findViewById(R.id.unlock_prompt_footer).getVisibility())
+ .isEqualTo(View.GONE);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 0ae012cd5e90..420c7ae3d2ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -72,9 +72,11 @@ import com.android.systemui.statusbar.notification.collection.render.SectionHead
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -92,6 +94,8 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
/**
* Tests for {@link NotificationStackScrollLayoutController}.
*/
@@ -138,6 +142,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private FeatureFlags mFeatureFlags;
@Mock private NotificationTargetsHelper mNotificationTargetsHelper;
@Mock private SecureSettings mSecureSettings;
+ @Mock private NotificationIconAreaController mIconAreaController;
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
@@ -250,6 +255,34 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
}
@Test
+ public void testUpdateEmptyShadeView_bouncerShowing_hideEmptyView() {
+ when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
+ initController(/* viewIsAttached= */ true);
+
+ when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
+ setupShowEmptyShadeViewState(true);
+ reset(mNotificationStackScrollLayout);
+ mController.updateShowEmptyShadeView();
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ /* visible= */ false,
+ /* areNotificationsHiddenInShade= */ false);
+ }
+
+ @Test
+ public void testUpdateEmptyShadeView_bouncerNotShowing_showEmptyView() {
+ when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
+ initController(/* viewIsAttached= */ true);
+
+ when(mCentralSurfaces.isBouncerShowing()).thenReturn(false);
+ setupShowEmptyShadeViewState(true);
+ reset(mNotificationStackScrollLayout);
+ mController.updateShowEmptyShadeView();
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(
+ /* visible= */ true,
+ /* areNotificationsHiddenInShade= */ false);
+ }
+
+ @Test
public void testOnUserChange_verifySensitiveProfile() {
when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true);
initController(/* viewIsAttached= */ true);
@@ -430,6 +463,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mKeyguardBypassController,
mZenModeController,
mNotificationLockscreenUserManager,
+ Optional.<NotificationListViewModel>empty(),
mMetricsLogger,
mDumpManager,
new FalsingCollectorFake(),
@@ -453,6 +487,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mStackLogger,
mLogger,
mNotificationStackSizeCalculator,
+ mIconAreaController,
mFeatureFlags,
mNotificationTargetsHelper,
mSecureSettings,
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 219e6a995680..c83769d84d2a 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
@@ -101,6 +101,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.charging.WiredChargingRippleController;
import com.android.systemui.classifier.FalsingCollectorFake;
@@ -138,6 +139,7 @@ import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.ShadeLogger;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.LightRevealScrim;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -231,10 +233,12 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private IStatusBarService mBarService;
@Mock private IDreamManager mDreamManager;
@Mock private LightRevealScrimViewModel mLightRevealScrimViewModel;
+ @Mock private LightRevealScrim mLightRevealScrim;
@Mock private ScrimController mScrimController;
@Mock private DozeScrimController mDozeScrimController;
@Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@Mock private BiometricUnlockController mBiometricUnlockController;
+ @Mock private AuthRippleController mAuthRippleController;
@Mock private NotificationListener mNotificationListener;
@Mock private KeyguardViewMediator mKeyguardViewMediator;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
@@ -345,6 +349,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true);
// For the Shade to animate during the Back gesture, we must enable the animation flag.
mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true);
+ mFeatureFlags.set(Flags.LIGHT_REVEAL_MIGRATION, true);
IThermalService thermalService = mock(IThermalService.class);
mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
@@ -497,6 +502,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mScrimController,
mLockscreenWallpaperLazy,
mBiometricUnlockControllerLazy,
+ mAuthRippleController,
mDozeServiceHost,
mPowerManager, mScreenPinningRequest,
mDozeScrimController,
@@ -538,6 +544,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mDreamManager,
mCameraLauncherLazy,
() -> mLightRevealScrimViewModel,
+ mLightRevealScrim,
mAlternateBouncerInteractor,
mUserTracker,
() -> mFingerprintManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 780e0c56a239..6fda56c8717a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -116,7 +116,6 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest {
.thenReturn(TEST_AUTO_DISMISS_TIME);
when(mVSProvider.isReorderingAllowed()).thenReturn(true);
mDependency.injectMockDependency(NotificationShadeWindowController.class);
- mDependency.injectMockDependency(ConfigurationController.class);
super.setUp();
mHeadsUpManager = new TestableHeadsUpManagerPhone(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index d30e0246c2dd..dc68180d962d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -994,7 +994,7 @@ class WifiRepositoryImplTest : SysuiTestCase() {
}
@Test
- fun wifiNetwork_currentNetworkLost_flowHasNoNetwork() =
+ fun wifiNetwork_currentActiveNetworkLost_flowHasNoNetwork() =
testScope.runTest {
var latest: WifiNetworkModel? = null
val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
@@ -1012,6 +1012,33 @@ class WifiRepositoryImplTest : SysuiTestCase() {
job.cancel()
}
+ /** Possible regression test for b/278618530. */
+ @Test
+ fun wifiNetwork_currentCarrierMergedNetworkLost_flowHasNoNetwork() =
+ testScope.runTest {
+ var latest: WifiNetworkModel? = null
+ val job = underTest.wifiNetwork.onEach { latest = it }.launchIn(this)
+
+ val wifiInfo =
+ mock<WifiInfo>().apply {
+ whenever(this.isPrimary).thenReturn(true)
+ whenever(this.isCarrierMerged).thenReturn(true)
+ }
+
+ getNetworkCallback()
+ .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo))
+ assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue()
+ assertThat((latest as WifiNetworkModel.CarrierMerged).networkId).isEqualTo(NETWORK_ID)
+
+ // WHEN we lose our current network
+ getNetworkCallback().onLost(NETWORK)
+
+ // THEN we update to no network
+ assertThat(latest is WifiNetworkModel.Inactive).isTrue()
+
+ job.cancel()
+ }
+
@Test
fun wifiNetwork_unknownNetworkLost_flowHasPreviousNetwork() =
testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
index 71ac7c48d5ea..683136d7af0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
@@ -45,6 +45,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.Map;
import java.util.function.Consumer;
@@ -53,16 +55,18 @@ import java.util.function.Consumer;
@SmallTest
public class ExtensionControllerImplTest extends SysuiTestCase {
+ @Mock
+ private ConfigurationController mConfigurationController;
+
private PluginManager mPluginManager;
private TunerService mTunerService;
private ExtensionController mExtensionController;
- private ConfigurationController mConfigurationController;
@Before
public void setup() {
+ MockitoAnnotations.initMocks(this);
mPluginManager = mDependency.injectMockDependency(PluginManager.class);
mTunerService = mDependency.injectMockDependency(TunerService.class);
- mConfigurationController = mDependency.injectMockDependency(ConfigurationController.class);
mExtensionController = new ExtensionControllerImpl(
mContext,
mock(LeakDetector.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
new file mode 100644
index 000000000000..af1d7881195e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
@@ -0,0 +1,128 @@
+package com.android.systemui.wallet.controller
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.drawable.Icon
+import android.os.Looper
+import android.service.quickaccesswallet.WalletCard
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.anySet
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+class WalletContextualLocationsServiceTest : SysuiTestCase() {
+ @Mock private lateinit var controller: WalletContextualSuggestionsController
+ private var featureFlags = FakeFeatureFlags()
+ private lateinit var underTest: WalletContextualLocationsService
+ private lateinit var testScope: TestScope
+ private var listenerRegisteredCount: Int = 0
+ private val listener: IWalletCardsUpdatedListener.Stub = object : IWalletCardsUpdatedListener.Stub() {
+ override fun registerNewWalletCards(cards: List<WalletCard?>) {
+ listenerRegisteredCount++
+ }
+ }
+
+ @Before
+ @kotlinx.coroutines.ExperimentalCoroutinesApi
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ doReturn(fakeWalletCards).whenever(controller).allWalletCards
+ doNothing().whenever(controller).setSuggestionCardIds(anySet())
+
+ if (Looper.myLooper() == null) Looper.prepare()
+
+ testScope = TestScope()
+ featureFlags.set(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS, true)
+ listenerRegisteredCount = 0
+
+ underTest = WalletContextualLocationsService(controller, featureFlags, testScope.backgroundScope)
+ }
+
+ @Test
+ @kotlinx.coroutines.ExperimentalCoroutinesApi
+ fun addListener() = testScope.runTest {
+ underTest.addWalletCardsUpdatedListenerInternal(listener)
+ assertThat(listenerRegisteredCount).isEqualTo(1)
+ }
+
+ @Test
+ @kotlinx.coroutines.ExperimentalCoroutinesApi
+ fun addStoreLocations() = testScope.runTest {
+ underTest.onWalletContextualLocationsStateUpdatedInternal(ArrayList<String>())
+ verify(controller, times(1)).setSuggestionCardIds(anySet())
+ }
+
+ @Test
+ @kotlinx.coroutines.ExperimentalCoroutinesApi
+ fun updateListenerAndLocationsState() = testScope.runTest {
+ // binds to the service and adds a listener
+ val underTestStub = getInterface
+ underTestStub.addWalletCardsUpdatedListener(listener)
+ assertThat(listenerRegisteredCount).isEqualTo(1)
+
+ // sends a list of card IDs to the controller
+ underTestStub.onWalletContextualLocationsStateUpdated(ArrayList<String>())
+ verify(controller, times(1)).setSuggestionCardIds(anySet())
+
+ // adds another listener
+ fakeWalletCards.update{ updatedFakeWalletCards }
+ runCurrent()
+ assertThat(listenerRegisteredCount).isEqualTo(2)
+
+ // sends another list of card IDs to the controller
+ underTestStub.onWalletContextualLocationsStateUpdated(ArrayList<String>())
+ verify(controller, times(2)).setSuggestionCardIds(anySet())
+ }
+
+ private val fakeWalletCards: MutableStateFlow<List<WalletCard>>
+ get() {
+ val intent = Intent(getContext(), WalletContextualLocationsService::class.java)
+ val pi: PendingIntent = PendingIntent.getActivity(getContext(), 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ val icon: Icon = Icon.createWithBitmap(Bitmap.createBitmap(70, 50, Bitmap.Config.ARGB_8888))
+ val walletCards: ArrayList<WalletCard> = ArrayList<WalletCard>()
+ walletCards.add(WalletCard.Builder("card1", icon, "card", pi).build())
+ walletCards.add(WalletCard.Builder("card2", icon, "card", pi).build())
+ return MutableStateFlow<List<WalletCard>>(walletCards)
+ }
+
+ private val updatedFakeWalletCards: List<WalletCard>
+ get() {
+ val intent = Intent(getContext(), WalletContextualLocationsService::class.java)
+ val pi: PendingIntent = PendingIntent.getActivity(getContext(), 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ val icon: Icon = Icon.createWithBitmap(Bitmap.createBitmap(70, 50, Bitmap.Config.ARGB_8888))
+ val walletCards: ArrayList<WalletCard> = ArrayList<WalletCard>()
+ walletCards.add(WalletCard.Builder("card3", icon, "card", pi).build())
+ return walletCards
+ }
+
+ private val getInterface: IWalletContextualLocationsService
+ get() {
+ val intent = Intent()
+ return IWalletContextualLocationsService.Stub.asInterface(underTest.onBind(intent))
+ }
+} \ No newline at end of file
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 bc3a5b7975a7..1510ee89dcd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -115,6 +115,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
+import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -398,7 +399,7 @@ public class BubblesTest extends SysuiTestCase {
mock(INotificationManager.class),
mIDreamManager,
mVisibilityProvider,
- interruptionStateProvider,
+ new NotificationInterruptStateProviderWrapper(interruptionStateProvider),
mZenModeController,
mLockscreenUserManager,
mCommonNotifCollection,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 8cae99893496..9de7a87c8b82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -128,7 +128,7 @@ public class WMShellTest extends SysuiTestCase {
@Test
public void initDesktopMode_registersListener() {
mWMShell.initDesktopMode(mDesktopMode);
- verify(mDesktopMode).addListener(
+ verify(mDesktopMode).addVisibleTasksListener(
any(DesktopModeTaskRepository.VisibleTasksListener.class),
any(Executor.class));
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt
index 5b431e72e2ac..09830413bdc8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt
@@ -14,6 +14,8 @@
package com.android.systemui.animation
+import com.android.app.animation.Interpolators
+
/** A [LaunchAnimator] to be used in tests. */
fun fakeLaunchAnimator(): LaunchAnimator {
return LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 9a257e54cf41..777c7c8e0da9 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -1417,20 +1417,29 @@ public class TouchExplorer extends BaseEventStreamTransformation
mSendTouchExplorationEndDelayed.forceSendAndRemove();
}
}
- if (!mState.isTouchInteracting()) {
+ if (!mState.isTouchInteracting() && !mState.isDragging()) {
// It makes no sense to delegate.
- Slog.e(LOG_TAG, "Error: Trying to delegate from "
- + mState.getStateSymbolicName(mState.getState()));
+ Slog.e(
+ LOG_TAG,
+ "Error: Trying to delegate from "
+ + mState.getStateSymbolicName(mState.getState()));
return;
}
- mState.startDelegating();
- MotionEvent prototype = mState.getLastReceivedEvent();
- if (prototype == null) {
+ MotionEvent event = mState.getLastReceivedEvent();
+ MotionEvent rawEvent = mState.getLastReceivedRawEvent();
+ if (event == null || rawEvent == null) {
Slog.d(LOG_TAG, "Unable to start delegating: unable to get last received event.");
return;
}
int policyFlags = mState.getLastReceivedPolicyFlags();
- mDispatcher.sendDownForAllNotInjectedPointers(prototype, policyFlags);
+ if (mState.isDragging()) {
+ // Send an event to the end of the drag gesture.
+ mDispatcher.sendMotionEvent(
+ event, ACTION_UP, rawEvent, ALL_POINTER_ID_BITS, policyFlags);
+ }
+ mState.startDelegating();
+ // Deliver all pointers to the view hierarchy.
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java b/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java
index 16d2e6b47a54..93531ddea005 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java
@@ -16,76 +16,31 @@
package com.android.server.accessibility.magnification;
-import android.annotation.NonNull;
import android.provider.DeviceConfig;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.concurrent.Executor;
-
/**
* Encapsulates the feature flags for always on magnification. {@see DeviceConfig}
*
* @hide
*/
-public class AlwaysOnMagnificationFeatureFlag {
+public class AlwaysOnMagnificationFeatureFlag extends MagnificationFeatureFlagBase {
private static final String NAMESPACE = DeviceConfig.NAMESPACE_WINDOW_MANAGER;
private static final String FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION =
"AlwaysOnMagnifier__enable_always_on_magnifier";
- private AlwaysOnMagnificationFeatureFlag() {}
-
- /** Returns true if the feature flag is enabled for always on magnification */
- public static boolean isAlwaysOnMagnificationEnabled() {
- return DeviceConfig.getBoolean(
- NAMESPACE,
- FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION,
- /* defaultValue= */ false);
+ @Override
+ String getNamespace() {
+ return NAMESPACE;
}
- /** Sets the feature flag. Only used for testing; requires shell permissions. */
- @VisibleForTesting
- public static boolean setAlwaysOnMagnificationEnabled(boolean isEnabled) {
- return DeviceConfig.setProperty(
- NAMESPACE,
- FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION,
- Boolean.toString(isEnabled),
- /* makeDefault= */ false);
- }
-
- /**
- * Adds a listener for when the feature flag changes.
- *
- * <p>{@see DeviceConfig#addOnPropertiesChangedListener(
- * String, Executor, DeviceConfig.OnPropertiesChangedListener)}
- */
- @NonNull
- public static DeviceConfig.OnPropertiesChangedListener addOnChangedListener(
- @NonNull Executor executor, @NonNull Runnable listener) {
- DeviceConfig.OnPropertiesChangedListener onChangedListener =
- properties -> {
- if (properties.getKeyset().contains(
- FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION)) {
- listener.run();
- }
- };
- DeviceConfig.addOnPropertiesChangedListener(
- NAMESPACE,
- executor,
- onChangedListener);
-
- return onChangedListener;
+ @Override
+ String getFeatureName() {
+ return FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION;
}
- /**
- * Remove a listener for when the feature flag changes.
- *
- * <p>{@see DeviceConfig#addOnPropertiesChangedListener(String, Executor,
- * DeviceConfig.OnPropertiesChangedListener)}
- */
- public static void removeOnChangedListener(
- @NonNull DeviceConfig.OnPropertiesChangedListener onChangedListener) {
- DeviceConfig.removeOnPropertiesChangedListener(onChangedListener);
+ @Override
+ boolean getDefaultValue() {
+ return false;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index ed8a35f45176..fbc7b3cbc63b 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -38,7 +38,6 @@ import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.Message;
-import android.provider.DeviceConfig;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.MathUtils;
@@ -57,6 +56,7 @@ import com.android.internal.R;
import com.android.internal.accessibility.common.MagnificationConstants;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService;
@@ -110,6 +110,7 @@ public class FullScreenMagnificationController implements
private boolean mAlwaysOnMagnificationEnabled = false;
private final DisplayManagerInternal mDisplayManagerInternal;
+ private final MagnificationThumbnailFeatureFlag mMagnificationThumbnailFeatureFlag;
@NonNull private final Supplier<MagnificationThumbnail> mThumbnailSupplier;
/**
@@ -177,9 +178,7 @@ public class FullScreenMagnificationController implements
mDisplayId, mMagnificationRegion);
mMagnificationRegion.getBounds(mMagnificationBounds);
- if (mMagnificationThumbnail == null) {
- mMagnificationThumbnail = mThumbnailSupplier.get();
- }
+ createThumbnailIfSupported();
return true;
}
@@ -207,7 +206,7 @@ public class FullScreenMagnificationController implements
mRegistered = false;
unregisterCallbackLocked(mDisplayId, delete);
- destroyThumbNail();
+ destroyThumbnail();
}
mUnregisterPending = false;
}
@@ -345,7 +344,7 @@ public class FullScreenMagnificationController implements
mMagnificationRegion.set(magnified);
mMagnificationRegion.getBounds(mMagnificationBounds);
- refreshThumbNail(getScale(), getCenterX(), getCenterY());
+ refreshThumbnail(getScale(), getCenterX(), getCenterY());
// It's possible that our magnification spec is invalid with the new bounds.
// Adjust the current spec's offsets if necessary.
@@ -405,9 +404,9 @@ public class FullScreenMagnificationController implements
}
if (isActivated()) {
- updateThumbNail(scale, centerX, centerY);
+ updateThumbnail(scale, centerX, centerY);
} else {
- hideThumbNail();
+ hideThumbnail();
}
}
@@ -538,7 +537,7 @@ public class FullScreenMagnificationController implements
mIdOfLastServiceToMagnify = INVALID_SERVICE_ID;
sendSpecToAnimation(spec, animationCallback);
- hideThumbNail();
+ hideThumbnail();
return changed;
}
@@ -596,16 +595,16 @@ public class FullScreenMagnificationController implements
}
@GuardedBy("mLock")
- void updateThumbNail(float scale, float centerX, float centerY) {
+ void updateThumbnail(float scale, float centerX, float centerY) {
if (mMagnificationThumbnail != null) {
- mMagnificationThumbnail.updateThumbNail(scale, centerX, centerY);
+ mMagnificationThumbnail.updateThumbnail(scale, centerX, centerY);
}
}
@GuardedBy("mLock")
- void refreshThumbNail(float scale, float centerX, float centerY) {
+ void refreshThumbnail(float scale, float centerX, float centerY) {
if (mMagnificationThumbnail != null) {
- mMagnificationThumbnail.setThumbNailBounds(
+ mMagnificationThumbnail.setThumbnailBounds(
mMagnificationBounds,
scale,
centerX,
@@ -615,20 +614,38 @@ public class FullScreenMagnificationController implements
}
@GuardedBy("mLock")
- void hideThumbNail() {
+ void hideThumbnail() {
if (mMagnificationThumbnail != null) {
- mMagnificationThumbnail.hideThumbNail();
+ mMagnificationThumbnail.hideThumbnail();
+ }
+ }
+
+ @GuardedBy("mLock")
+ void createThumbnailIfSupported() {
+ if (mMagnificationThumbnail == null) {
+ mMagnificationThumbnail = mThumbnailSupplier.get();
+ // We call refreshThumbnail when the thumbnail is just created to set current
+ // magnification bounds to thumbnail. It to prevent the thumbnail size has not yet
+ // updated properly and thus shows with huge size. (b/276314641)
+ refreshThumbnail(getScale(), getCenterX(), getCenterY());
}
}
@GuardedBy("mLock")
- void destroyThumbNail() {
+ void destroyThumbnail() {
if (mMagnificationThumbnail != null) {
- hideThumbNail();
+ hideThumbnail();
mMagnificationThumbnail = null;
}
}
+ void onThumbnailFeatureFlagChanged() {
+ synchronized (mLock) {
+ destroyThumbnail();
+ createThumbnailIfSupported();
+ }
+ }
+
/**
* Updates the current magnification spec.
*
@@ -768,20 +785,7 @@ public class FullScreenMagnificationController implements
lock,
magnificationInfoChangedCallback,
scaleProvider,
- () -> {
- if (DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_ACCESSIBILITY,
- "enable_magnifier_thumbnail",
- /* defaultValue= */ false)) {
- return new MagnificationThumbnail(
- context,
- context.getSystemService(WindowManager.class),
- new Handler(context.getMainLooper())
- );
- }
-
- return null;
- });
+ /* thumbnailSupplier= */ null);
}
/** Constructor for tests */
@@ -791,7 +795,7 @@ public class FullScreenMagnificationController implements
@NonNull Object lock,
@NonNull MagnificationInfoChangedCallback magnificationInfoChangedCallback,
@NonNull MagnificationScaleProvider scaleProvider,
- @NonNull Supplier<MagnificationThumbnail> thumbnailSupplier) {
+ Supplier<MagnificationThumbnail> thumbnailSupplier) {
mControllerCtx = ctx;
mLock = lock;
mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId();
@@ -799,7 +803,41 @@ public class FullScreenMagnificationController implements
addInfoChangedCallback(magnificationInfoChangedCallback);
mScaleProvider = scaleProvider;
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
- mThumbnailSupplier = thumbnailSupplier;
+ mMagnificationThumbnailFeatureFlag = new MagnificationThumbnailFeatureFlag();
+ mMagnificationThumbnailFeatureFlag.addOnChangedListener(
+ ConcurrentUtils.DIRECT_EXECUTOR, this::onMagnificationThumbnailFeatureFlagChanged);
+ if (thumbnailSupplier != null) {
+ mThumbnailSupplier = thumbnailSupplier;
+ } else {
+ mThumbnailSupplier = () -> {
+ if (mMagnificationThumbnailFeatureFlag.isFeatureFlagEnabled()) {
+ return new MagnificationThumbnail(
+ ctx.getContext(),
+ ctx.getContext().getSystemService(WindowManager.class),
+ new Handler(ctx.getContext().getMainLooper())
+ );
+ }
+ return null;
+ };
+ }
+ }
+
+ private void onMagnificationThumbnailFeatureFlagChanged() {
+ synchronized (mLock) {
+ for (int i = 0; i < mDisplays.size(); i++) {
+ onMagnificationThumbnailFeatureFlagChanged(mDisplays.keyAt(i));
+ }
+ }
+ }
+
+ private void onMagnificationThumbnailFeatureFlagChanged(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return;
+ }
+ display.onThumbnailFeatureFlagChanged();
+ }
}
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index c1c47f53ab7e..7ee72dfa30fd 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -93,6 +93,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
private final SparseArray<DisableMagnificationCallback>
mMagnificationEndRunnableSparseArray = new SparseArray();
+ private final AlwaysOnMagnificationFeatureFlag mAlwaysOnMagnificationFeatureFlag;
private final MagnificationScaleProvider mScaleProvider;
private FullScreenMagnificationController mFullScreenMagnificationController;
private WindowMagnificationManager mWindowMagnificationMgr;
@@ -151,7 +152,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
FEATURE_WINDOW_MAGNIFICATION);
- AlwaysOnMagnificationFeatureFlag.addOnChangedListener(
+ mAlwaysOnMagnificationFeatureFlag = new AlwaysOnMagnificationFeatureFlag();
+ mAlwaysOnMagnificationFeatureFlag.addOnChangedListener(
ConcurrentUtils.DIRECT_EXECUTOR, mAms::updateAlwaysOnMagnification);
}
@@ -710,7 +712,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
}
public boolean isAlwaysOnMagnificationFeatureFlagEnabled() {
- return AlwaysOnMagnificationFeatureFlag.isAlwaysOnMagnificationEnabled();
+ return mAlwaysOnMagnificationFeatureFlag.isFeatureFlagEnabled();
}
private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationFeatureFlagBase.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationFeatureFlagBase.java
new file mode 100644
index 000000000000..2965887d8eb3
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationFeatureFlagBase.java
@@ -0,0 +1,115 @@
+/*
+ * 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.server.accessibility.magnification;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.provider.DeviceConfig;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Abstract base class to encapsulates the feature flags for magnification features.
+ * {@see DeviceConfig}
+ *
+ * @hide
+ */
+abstract class MagnificationFeatureFlagBase {
+
+ abstract String getNamespace();
+ abstract String getFeatureName();
+ abstract boolean getDefaultValue();
+
+ private void clearCallingIdentifyAndTryCatch(Runnable tryBlock, Runnable catchBlock) {
+ try {
+ Binder.withCleanCallingIdentity(() -> tryBlock.run());
+ } catch (Throwable throwable) {
+ catchBlock.run();
+ }
+ }
+
+ /** Returns true iff the feature flag is readable and enabled */
+ public boolean isFeatureFlagEnabled() {
+ AtomicBoolean isEnabled = new AtomicBoolean(getDefaultValue());
+
+ clearCallingIdentifyAndTryCatch(
+ () -> isEnabled.set(DeviceConfig.getBoolean(
+ getNamespace(),
+ getFeatureName(),
+ getDefaultValue())),
+ () -> isEnabled.set(getDefaultValue()));
+
+ return isEnabled.get();
+ }
+
+ /** Sets the feature flag. Only used for testing; requires shell permissions. */
+ @VisibleForTesting
+ public boolean setFeatureFlagEnabled(boolean isEnabled) {
+ AtomicBoolean success = new AtomicBoolean(getDefaultValue());
+
+ clearCallingIdentifyAndTryCatch(
+ () -> success.set(DeviceConfig.setProperty(
+ getNamespace(),
+ getFeatureName(),
+ Boolean.toString(isEnabled),
+ /* makeDefault= */ false)),
+ () -> success.set(getDefaultValue()));
+
+ return success.get();
+ }
+
+ /**
+ * Adds a listener for when the feature flag changes.
+ *
+ * <p>{@see DeviceConfig#addOnPropertiesChangedListener(
+ * String, Executor, DeviceConfig.OnPropertiesChangedListener)}
+ */
+ @NonNull
+ public DeviceConfig.OnPropertiesChangedListener addOnChangedListener(
+ @NonNull Executor executor, @NonNull Runnable listener) {
+ DeviceConfig.OnPropertiesChangedListener onChangedListener =
+ properties -> {
+ if (properties.getKeyset().contains(
+ getFeatureName())) {
+ listener.run();
+ }
+ };
+
+ clearCallingIdentifyAndTryCatch(
+ () -> DeviceConfig.addOnPropertiesChangedListener(
+ getNamespace(),
+ executor,
+ onChangedListener),
+ () -> {});
+
+ return onChangedListener;
+ }
+
+ /**
+ * Remove a listener for when the feature flag changes.
+ *
+ * <p>{@see DeviceConfig#addOnPropertiesChangedListener(String, Executor,
+ * DeviceConfig.OnPropertiesChangedListener)}
+ */
+ public void removeOnChangedListener(
+ @NonNull DeviceConfig.OnPropertiesChangedListener onChangedListener) {
+ DeviceConfig.removeOnPropertiesChangedListener(onChangedListener);
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java
index 5a783f47ccdc..03fa93d8a3bc 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java
@@ -58,7 +58,9 @@ public class MagnificationThumbnail {
@VisibleForTesting
public final FrameLayout mThumbnailLayout;
- private final View mThumbNailView;
+ private final View mThumbnailView;
+ private int mThumbnailWidth;
+ private int mThumbnailHeight;
private final WindowManager.LayoutParams mBackgroundParams;
private boolean mVisible = false;
@@ -66,7 +68,7 @@ public class MagnificationThumbnail {
private static final float ASPECT_RATIO = 14f;
private static final float BG_ASPECT_RATIO = ASPECT_RATIO / 2f;
- private ObjectAnimator mThumbNailAnimator;
+ private ObjectAnimator mThumbnailAnimator;
private boolean mIsFadingIn;
/**
@@ -79,9 +81,11 @@ public class MagnificationThumbnail {
mWindowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
mThumbnailLayout = (FrameLayout) LayoutInflater.from(mContext)
.inflate(R.layout.thumbnail_background_view, /* root: */ null);
- mThumbNailView =
+ mThumbnailView =
mThumbnailLayout.findViewById(R.id.accessibility_magnification_thumbnail_view);
mBackgroundParams = createLayoutParams();
+ mThumbnailWidth = 0;
+ mThumbnailHeight = 0;
}
/**
@@ -90,35 +94,35 @@ public class MagnificationThumbnail {
* @param currentBounds the current magnification bounds
*/
@AnyThread
- public void setThumbNailBounds(Rect currentBounds, float scale, float centerX, float centerY) {
+ public void setThumbnailBounds(Rect currentBounds, float scale, float centerX, float centerY) {
if (DEBUG) {
- Log.d(LOG_TAG, "setThumbNailBounds " + currentBounds);
+ Log.d(LOG_TAG, "setThumbnailBounds " + currentBounds);
}
mHandler.post(() -> {
mWindowBounds = currentBounds;
setBackgroundBounds();
if (mVisible) {
- updateThumbNailMainThread(scale, centerX, centerY);
+ updateThumbnailMainThread(scale, centerX, centerY);
}
});
}
private void setBackgroundBounds() {
Point magnificationBoundary = getMagnificationThumbnailPadding(mContext);
- final int thumbNailWidth = (int) (mWindowBounds.width() / BG_ASPECT_RATIO);
- final int thumbNailHeight = (int) (mWindowBounds.height() / BG_ASPECT_RATIO);
+ mThumbnailWidth = (int) (mWindowBounds.width() / BG_ASPECT_RATIO);
+ mThumbnailHeight = (int) (mWindowBounds.height() / BG_ASPECT_RATIO);
int initX = magnificationBoundary.x;
int initY = magnificationBoundary.y;
- mBackgroundParams.width = thumbNailWidth;
- mBackgroundParams.height = thumbNailHeight;
+ mBackgroundParams.width = mThumbnailWidth;
+ mBackgroundParams.height = mThumbnailHeight;
mBackgroundParams.x = initX;
mBackgroundParams.y = initY;
}
@MainThread
- private void showThumbNail() {
+ private void showThumbnail() {
if (DEBUG) {
- Log.d(LOG_TAG, "showThumbNail " + mVisible);
+ Log.d(LOG_TAG, "showThumbnail " + mVisible);
}
animateThumbnail(true);
}
@@ -127,14 +131,14 @@ public class MagnificationThumbnail {
* Hides thumbnail and removes the view from the window when finished animating.
*/
@AnyThread
- public void hideThumbNail() {
- mHandler.post(this::hideThumbNailMainThread);
+ public void hideThumbnail() {
+ mHandler.post(this::hideThumbnailMainThread);
}
@MainThread
- private void hideThumbNailMainThread() {
+ private void hideThumbnailMainThread() {
if (DEBUG) {
- Log.d(LOG_TAG, "hideThumbNail " + mVisible);
+ Log.d(LOG_TAG, "hideThumbnail " + mVisible);
}
if (mVisible) {
animateThumbnail(false);
@@ -155,14 +159,14 @@ public class MagnificationThumbnail {
+ " fadeIn: " + fadeIn
+ " mVisible: " + mVisible
+ " isFadingIn: " + mIsFadingIn
- + " isRunning: " + mThumbNailAnimator
+ + " isRunning: " + mThumbnailAnimator
);
}
// Reset countdown to hide automatically
- mHandler.removeCallbacks(this::hideThumbNailMainThread);
+ mHandler.removeCallbacks(this::hideThumbnailMainThread);
if (fadeIn) {
- mHandler.postDelayed(this::hideThumbNailMainThread, LINGER_DURATION_MS);
+ mHandler.postDelayed(this::hideThumbnailMainThread, LINGER_DURATION_MS);
}
if (fadeIn == mIsFadingIn) {
@@ -175,18 +179,18 @@ public class MagnificationThumbnail {
mVisible = true;
}
- if (mThumbNailAnimator != null) {
- mThumbNailAnimator.cancel();
+ if (mThumbnailAnimator != null) {
+ mThumbnailAnimator.cancel();
}
- mThumbNailAnimator = ObjectAnimator.ofFloat(
+ mThumbnailAnimator = ObjectAnimator.ofFloat(
mThumbnailLayout,
"alpha",
fadeIn ? 1f : 0f
);
- mThumbNailAnimator.setDuration(
+ mThumbnailAnimator.setDuration(
fadeIn ? FADE_IN_ANIMATION_DURATION_MS : FADE_OUT_ANIMATION_DURATION_MS
);
- mThumbNailAnimator.addListener(new Animator.AnimatorListener() {
+ mThumbnailAnimator.addListener(new Animator.AnimatorListener() {
private boolean mIsCancelled;
@Override
@@ -231,7 +235,7 @@ public class MagnificationThumbnail {
}
});
- mThumbNailAnimator.start();
+ mThumbnailAnimator.start();
}
/**
@@ -246,38 +250,48 @@ public class MagnificationThumbnail {
* of the viewport, or {@link Float#NaN} to leave unchanged
*/
@AnyThread
- public void updateThumbNail(float scale, float centerX, float centerY) {
- mHandler.post(() -> updateThumbNailMainThread(scale, centerX, centerY));
+ public void updateThumbnail(float scale, float centerX, float centerY) {
+ mHandler.post(() -> updateThumbnailMainThread(scale, centerX, centerY));
}
@MainThread
- private void updateThumbNailMainThread(float scale, float centerX, float centerY) {
+ private void updateThumbnailMainThread(float scale, float centerX, float centerY) {
// Restart the fadeout countdown (or show if it's hidden)
- showThumbNail();
+ showThumbnail();
- var scaleDown = Float.isNaN(scale) ? mThumbNailView.getScaleX() : 1f / scale;
+ var scaleDown = Float.isNaN(scale) ? mThumbnailView.getScaleX() : 1f / scale;
if (!Float.isNaN(scale)) {
- mThumbNailView.setScaleX(scaleDown);
- mThumbNailView.setScaleY(scaleDown);
+ mThumbnailView.setScaleX(scaleDown);
+ mThumbnailView.setScaleY(scaleDown);
+ }
+ float thumbnailWidth;
+ float thumbnailHeight;
+ if (mThumbnailView.getWidth() == 0 || mThumbnailView.getHeight() == 0) {
+ // if the thumbnail view size is not updated correctly, we just use the cached values.
+ thumbnailWidth = mThumbnailWidth;
+ thumbnailHeight = mThumbnailHeight;
+ } else {
+ thumbnailWidth = mThumbnailView.getWidth();
+ thumbnailHeight = mThumbnailView.getHeight();
}
if (!Float.isNaN(centerX)) {
- var padding = mThumbNailView.getPaddingTop();
+ var padding = mThumbnailView.getPaddingTop();
var ratio = 1f / BG_ASPECT_RATIO;
- var centerXScaled = centerX * ratio - (mThumbNailView.getWidth() / 2f + padding);
- var centerYScaled = centerY * ratio - (mThumbNailView.getHeight() / 2f + padding);
+ var centerXScaled = centerX * ratio - (thumbnailWidth / 2f + padding);
+ var centerYScaled = centerY * ratio - (thumbnailHeight / 2f + padding);
if (DEBUG) {
Log.d(
LOG_TAG,
- "updateThumbNail centerXScaled : " + centerXScaled
+ "updateThumbnail centerXScaled : " + centerXScaled
+ " centerYScaled : " + centerYScaled
- + " getTranslationX : " + mThumbNailView.getTranslationX()
+ + " getTranslationX : " + mThumbnailView.getTranslationX()
+ " ratio : " + ratio
);
}
- mThumbNailView.setTranslationX(centerXScaled);
- mThumbNailView.setTranslationY(centerYScaled);
+ mThumbnailView.setTranslationX(centerXScaled);
+ mThumbnailView.setTranslationY(centerYScaled);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java
new file mode 100644
index 000000000000..519f31b86f78
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java
@@ -0,0 +1,46 @@
+/*
+ * 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.server.accessibility.magnification;
+
+import android.provider.DeviceConfig;
+
+/**
+ * Encapsulates the feature flags for magnification thumbnail. {@see DeviceConfig}
+ *
+ * @hide
+ */
+public class MagnificationThumbnailFeatureFlag extends MagnificationFeatureFlagBase {
+
+ private static final String NAMESPACE = DeviceConfig.NAMESPACE_ACCESSIBILITY;
+ private static final String FEATURE_NAME_ENABLE_MAGNIFIER_THUMBNAIL =
+ "enable_magnifier_thumbnail";
+
+ @Override
+ String getNamespace() {
+ return NAMESPACE;
+ }
+
+ @Override
+ String getFeatureName() {
+ return FEATURE_NAME_ENABLE_MAGNIFIER_THUMBNAIL;
+ }
+
+ @Override
+ boolean getDefaultValue() {
+ return false;
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3bd4547dd3e6..b2e8ffcd8fca 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -4277,7 +4277,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
getUiForShowing().showFillUi(filledId, response, filterText,
mService.getServicePackageName(), mComponentName,
- targetLabel, targetIcon, this, id, mCompatMode);
+ targetLabel, targetIcon, this, userId, id, mCompatMode);
synchronized (mLock) {
mPresentationStatsEventLogger.maybeSetCountShown(
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 829161037832..a6318186f2d5 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -23,6 +23,7 @@ import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -196,17 +197,19 @@ public final class AutoFillUI {
* @param serviceLabel label of autofill service
* @param serviceIcon icon of autofill service
* @param callback identifier for the caller
+ * @param userId the user associated wit the session
* @param sessionId id of the autofill session
* @param compatMode whether the app is being autofilled in compatibility mode.
*/
public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response,
@Nullable String filterText, @Nullable String servicePackageName,
@NonNull ComponentName componentName, @NonNull CharSequence serviceLabel,
- @NonNull Drawable serviceIcon, @NonNull AutoFillUiCallback callback, int sessionId,
- boolean compatMode) {
+ @NonNull Drawable serviceIcon, @NonNull AutoFillUiCallback callback,
+ @UserIdInt int userId, int sessionId, boolean compatMode) {
if (sDebug) {
final int size = filterText == null ? 0 : filterText.length();
- Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + size + " chars");
+ Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + size + " chars, userId="
+ + userId);
}
final LogMaker log = Helper
.newLogMaker(MetricsEvent.AUTOFILL_FILL_UI, componentName, servicePackageName,
@@ -221,7 +224,7 @@ public final class AutoFillUI {
return;
}
hideAllUiThread(callback);
- mFillUi = new FillUi(mContext, response, focusedId,
+ mFillUi = new FillUi(mContext, userId, response, focusedId,
filterText, mOverlayControl, serviceLabel, serviceIcon,
mUiModeMgr.isNightMode(),
new FillUi.Callback() {
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 76f4505bbb4a..30d2fe40158b 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -22,12 +22,15 @@ import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.content.Context;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
+import android.os.UserManager;
import android.service.autofill.Dataset;
import android.service.autofill.Dataset.DatasetFieldFilter;
import android.service.autofill.FillResponse;
@@ -36,6 +39,7 @@ import android.util.PluralsMessageFormatter;
import android.util.Slog;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
+import android.view.Display;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -57,9 +61,12 @@ import android.widget.RemoteViews;
import android.widget.TextView;
import com.android.internal.R;
+import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.autofill.AutofillManagerService;
import com.android.server.autofill.Helper;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.utils.Slogf;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -133,13 +140,29 @@ final class FillUi {
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
}
- FillUi(@NonNull Context context, @NonNull FillResponse response,
+ FillUi(@NonNull Context context, @UserIdInt int userId, @NonNull FillResponse response,
@NonNull AutofillId focusedViewId, @Nullable String filterText,
@NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel,
@NonNull Drawable serviceIcon, boolean nightMode, @NonNull Callback callback) {
if (sVerbose) Slog.v(TAG, "nightMode: " + nightMode);
mThemeId = nightMode ? THEME_ID_DARK : THEME_ID_LIGHT;
mCallback = callback;
+
+ if (UserManager.isVisibleBackgroundUsersEnabled()) {
+ UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+ int displayId = umi.getMainDisplayAssignedToUser(userId);
+ if (sDebug) {
+ Slogf.d(TAG, "Creating context for display %d for user %d", displayId, userId);
+ }
+ Display display = context.getSystemService(DisplayManager.class).getDisplay(displayId);
+ if (display != null) {
+ context = context.createDisplayContext(display);
+ } else {
+ Slogf.d(TAG, "Could not get display with id %d (which is associated with user %d; "
+ + "FillUi operations will probably fail", displayId, userId);
+ }
+ }
+
mFullScreen = isFullScreen(context);
mContext = new ContextThemeWrapper(context, mThemeId);
@@ -774,6 +797,7 @@ final class FillUi {
pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth);
pw.print(prefix); pw.print("mContentHeight: "); pw.println(mContentHeight);
pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
+ pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
pw.print(prefix); pw.print("theme id: "); pw.print(mThemeId);
switch (mThemeId) {
case THEME_ID_DARK:
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index efff11226683..d8fbd08a0e70 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -604,25 +604,21 @@ public class CompanionDeviceManagerService extends SystemService {
}
@Override
- @GuardedBy("CompanionDeviceManagerService.this.mTransportManager.mTransports")
public void addOnTransportsChangedListener(IOnTransportsChangedListener listener) {
mTransportManager.addListener(listener);
}
@Override
- @GuardedBy("CompanionDeviceManagerService.this.mTransportManager.mTransports")
public void removeOnTransportsChangedListener(IOnTransportsChangedListener listener) {
mTransportManager.removeListener(listener);
}
@Override
- @GuardedBy("CompanionDeviceManagerService.this.mTransportManager.mTransports")
public void sendMessage(int messageType, byte[] data, int[] associationIds) {
mTransportManager.sendMessage(messageType, data, associationIds);
}
@Override
- @GuardedBy("CompanionDeviceManagerService.this.mTransportManager.mTransports")
public void addOnMessageReceivedListener(int messageType,
IOnMessageReceivedListener listener) {
mTransportManager.addListener(messageType, listener);
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java
new file mode 100644
index 000000000000..6f99d8677646
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java
@@ -0,0 +1,332 @@
+/*
+ * 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.server.companion.datatransfer.contextsync;
+
+import android.content.ComponentName;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.telecom.Call;
+import android.telecom.Connection;
+import android.telecom.ConnectionService;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+
+/** Service for Telecom to bind to when call metadata is synced between devices. */
+public class CallMetadataSyncConnectionService extends ConnectionService {
+
+ private static final String TAG = "CallMetadataSyncConnectionService";
+
+ private AudioManager mAudioManager;
+ private TelecomManager mTelecomManager;
+ private final Map<PhoneAccountHandleIdentifier, PhoneAccountHandle> mPhoneAccountHandles =
+ new HashMap<>();
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ mAudioManager = getSystemService(AudioManager.class);
+ mTelecomManager = getSystemService(TelecomManager.class);
+ }
+
+ /**
+ * Registers a {@link android.telecom.PhoneAccount} for a given call-capable app on the synced
+ * device.
+ */
+ private void registerPhoneAccount(int associationId, String appIdentifier,
+ String humanReadableAppName) {
+ final PhoneAccountHandleIdentifier phoneAccountHandleIdentifier =
+ new PhoneAccountHandleIdentifier(associationId, appIdentifier);
+ final PhoneAccount phoneAccount = createPhoneAccount(phoneAccountHandleIdentifier,
+ humanReadableAppName);
+ mTelecomManager.registerPhoneAccount(phoneAccount);
+ mTelecomManager.enablePhoneAccount(mPhoneAccountHandles.get(phoneAccountHandleIdentifier),
+ true);
+ }
+
+ /**
+ * Unregisters a {@link android.telecom.PhoneAccount} for a given call-capable app on the synced
+ * device.
+ */
+ private void unregisterPhoneAccount(int associationId, String appIdentifier) {
+ mTelecomManager.unregisterPhoneAccount(mPhoneAccountHandles.remove(
+ new PhoneAccountHandleIdentifier(associationId, appIdentifier)));
+ }
+
+ @VisibleForTesting
+ PhoneAccount createPhoneAccount(PhoneAccountHandleIdentifier phoneAccountHandleIdentifier,
+ String humanReadableAppName) {
+ if (mPhoneAccountHandles.containsKey(phoneAccountHandleIdentifier)) {
+ // Already exists!
+ return null;
+ }
+ final PhoneAccountHandle handle = new PhoneAccountHandle(
+ new ComponentName(this, CallMetadataSyncConnectionService.class),
+ UUID.randomUUID().toString());
+ mPhoneAccountHandles.put(phoneAccountHandleIdentifier, handle);
+ return new PhoneAccount.Builder(handle, humanReadableAppName)
+ .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER
+ | PhoneAccount.CAPABILITY_SELF_MANAGED).build();
+ }
+
+ static final class PhoneAccountHandleIdentifier {
+ private final int mAssociationId;
+ private final String mAppIdentifier;
+
+ PhoneAccountHandleIdentifier(int associationId, String appIdentifier) {
+ mAssociationId = associationId;
+ mAppIdentifier = appIdentifier;
+ }
+
+ public int getAssociationId() {
+ return mAssociationId;
+ }
+
+ public String getAppIdentifier() {
+ return mAppIdentifier;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAssociationId, mAppIdentifier);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof PhoneAccountHandleIdentifier) {
+ return ((PhoneAccountHandleIdentifier) other).getAssociationId() == mAssociationId
+ && mAppIdentifier != null
+ && mAppIdentifier.equals(
+ ((PhoneAccountHandleIdentifier) other).getAppIdentifier());
+ }
+ return false;
+ }
+ }
+
+ private static final class CallMetadataSyncConnectionIdentifier {
+ private final int mAssociationId;
+ private final long mCallId;
+
+ CallMetadataSyncConnectionIdentifier(int associationId, long callId) {
+ mAssociationId = associationId;
+ mCallId = callId;
+ }
+
+ public int getAssociationId() {
+ return mAssociationId;
+ }
+
+ public long getCallId() {
+ return mCallId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAssociationId, mCallId);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof CallMetadataSyncConnectionIdentifier) {
+ return ((CallMetadataSyncConnectionIdentifier) other).getAssociationId()
+ == mAssociationId
+ && (((CallMetadataSyncConnectionIdentifier) other).getCallId() == mCallId);
+ }
+ return false;
+ }
+ }
+
+ private abstract static class CallMetadataSyncConnectionCallback {
+
+ abstract void sendCallAction(int associationId, long callId, int action);
+
+ abstract void sendStateChange(int associationId, long callId, int newState);
+ }
+
+ private static class CallMetadataSyncConnection extends Connection {
+
+ private final TelecomManager mTelecomManager;
+ private final AudioManager mAudioManager;
+ private final int mAssociationId;
+ private final CallMetadataSyncData.Call mCall;
+ private final CallMetadataSyncConnectionCallback mCallback;
+
+ CallMetadataSyncConnection(TelecomManager telecomManager, AudioManager audioManager,
+ int associationId, CallMetadataSyncData.Call call,
+ CallMetadataSyncConnectionCallback callback) {
+ mTelecomManager = telecomManager;
+ mAudioManager = audioManager;
+ mAssociationId = associationId;
+ mCall = call;
+ mCallback = callback;
+ }
+
+ public long getCallId() {
+ return mCall.getId();
+ }
+
+ public void initialize() {
+ final int status = mCall.getStatus();
+ if (status == android.companion.Telecom.Call.RINGING_SILENCED) {
+ mTelecomManager.silenceRinger();
+ }
+ final int state = CrossDeviceCall.convertStatusToState(status);
+ if (state == Call.STATE_RINGING) {
+ setRinging();
+ } else if (state == Call.STATE_ACTIVE) {
+ setActive();
+ } else if (state == Call.STATE_HOLDING) {
+ setOnHold();
+ } else {
+ Slog.e(TAG, "Could not initialize call to unknown state");
+ }
+
+ final Bundle extras = new Bundle();
+ extras.putLong(CrossDeviceCall.EXTRA_CALL_ID, mCall.getId());
+ putExtras(extras);
+
+ int capabilities = getConnectionCapabilities();
+ if (mCall.hasControl(android.companion.Telecom.Call.PUT_ON_HOLD)) {
+ capabilities |= CAPABILITY_HOLD;
+ } else {
+ capabilities &= ~CAPABILITY_HOLD;
+ }
+ if (mCall.hasControl(android.companion.Telecom.Call.MUTE)) {
+ capabilities |= CAPABILITY_MUTE;
+ } else {
+ capabilities &= ~CAPABILITY_MUTE;
+ }
+ mAudioManager.setMicrophoneMute(
+ mCall.hasControl(android.companion.Telecom.Call.UNMUTE));
+ if (capabilities != getConnectionCapabilities()) {
+ setConnectionCapabilities(capabilities);
+ }
+ }
+
+ public void update(CallMetadataSyncData.Call call) {
+ final int status = call.getStatus();
+ if (status == android.companion.Telecom.Call.RINGING_SILENCED
+ && mCall.getStatus() != android.companion.Telecom.Call.RINGING_SILENCED) {
+ mTelecomManager.silenceRinger();
+ }
+ mCall.setStatus(status);
+ final int state = CrossDeviceCall.convertStatusToState(status);
+ if (state != getState()) {
+ if (state == Call.STATE_RINGING) {
+ setRinging();
+ } else if (state == Call.STATE_ACTIVE) {
+ setActive();
+ } else if (state == Call.STATE_HOLDING) {
+ setOnHold();
+ } else {
+ Slog.e(TAG, "Could not update call to unknown state");
+ }
+ }
+
+ int capabilities = getConnectionCapabilities();
+ final boolean hasHoldControl = mCall.hasControl(
+ android.companion.Telecom.Call.PUT_ON_HOLD)
+ || mCall.hasControl(android.companion.Telecom.Call.TAKE_OFF_HOLD);
+ if (hasHoldControl != ((getConnectionCapabilities() & CAPABILITY_HOLD)
+ == CAPABILITY_HOLD)) {
+ if (hasHoldControl) {
+ capabilities |= CAPABILITY_HOLD;
+ } else {
+ capabilities &= ~CAPABILITY_HOLD;
+ }
+ }
+ final boolean hasMuteControl = mCall.hasControl(android.companion.Telecom.Call.MUTE);
+ if (hasMuteControl != ((getConnectionCapabilities() & CAPABILITY_MUTE)
+ == CAPABILITY_MUTE)) {
+ if (hasMuteControl) {
+ capabilities |= CAPABILITY_MUTE;
+ } else {
+ capabilities &= ~CAPABILITY_MUTE;
+ }
+ }
+ mAudioManager.setMicrophoneMute(
+ mCall.hasControl(android.companion.Telecom.Call.UNMUTE));
+ if (capabilities != getConnectionCapabilities()) {
+ setConnectionCapabilities(capabilities);
+ }
+ }
+
+ @Override
+ public void onAnswer(int videoState) {
+ sendCallAction(android.companion.Telecom.Call.ACCEPT);
+ }
+
+ @Override
+ public void onReject() {
+ sendCallAction(android.companion.Telecom.Call.REJECT);
+ }
+
+ @Override
+ public void onReject(int rejectReason) {
+ onReject();
+ }
+
+ @Override
+ public void onReject(String replyMessage) {
+ onReject();
+ }
+
+ @Override
+ public void onSilence() {
+ sendCallAction(android.companion.Telecom.Call.SILENCE);
+ }
+
+ @Override
+ public void onHold() {
+ sendCallAction(android.companion.Telecom.Call.PUT_ON_HOLD);
+ }
+
+ @Override
+ public void onUnhold() {
+ sendCallAction(android.companion.Telecom.Call.TAKE_OFF_HOLD);
+ }
+
+ @Override
+ public void onMuteStateChanged(boolean isMuted) {
+ sendCallAction(isMuted ? android.companion.Telecom.Call.MUTE
+ : android.companion.Telecom.Call.UNMUTE);
+ }
+
+ @Override
+ public void onDisconnect() {
+ sendCallAction(android.companion.Telecom.Call.END);
+ }
+
+ @Override
+ public void onStateChanged(int state) {
+ mCallback.sendStateChange(mAssociationId, mCall.getId(), state);
+ }
+
+ private void sendCallAction(int action) {
+ mCallback.sendCallAction(mAssociationId, mCall.getId(), action);
+ }
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java
new file mode 100644
index 000000000000..1e4bb9a504ba
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java
@@ -0,0 +1,188 @@
+/*
+ * 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.server.companion.datatransfer.contextsync;
+
+import android.annotation.NonNull;
+import android.companion.ContextSyncMessage;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/** A read-only snapshot of an {@link ContextSyncMessage}. */
+class CallMetadataSyncData {
+
+ final Map<Long, CallMetadataSyncData.Call> mCalls = new HashMap<>();
+ final List<CallMetadataSyncData.Call> mRequests = new ArrayList<>();
+
+ public void addCall(CallMetadataSyncData.Call call) {
+ mCalls.put(call.getId(), call);
+ }
+
+ public boolean hasCall(long id) {
+ return mCalls.containsKey(id);
+ }
+
+ public Collection<CallMetadataSyncData.Call> getCalls() {
+ return mCalls.values();
+ }
+
+ public void addRequest(CallMetadataSyncData.Call call) {
+ mRequests.add(call);
+ }
+
+ public List<CallMetadataSyncData.Call> getRequests() {
+ return mRequests;
+ }
+
+ public static class Call implements Parcelable {
+ private long mId;
+ private String mCallerId;
+ private byte[] mAppIcon;
+ private String mAppName;
+ private String mAppIdentifier;
+ private int mStatus;
+ private final Set<Integer> mControls = new HashSet<>();
+
+ public static Call fromParcel(Parcel parcel) {
+ final Call call = new Call();
+ call.setId(parcel.readLong());
+ call.setCallerId(parcel.readString());
+ call.setAppIcon(parcel.readBlob());
+ call.setAppName(parcel.readString());
+ call.setAppIdentifier(parcel.readString());
+ call.setStatus(parcel.readInt());
+ final int numberOfControls = parcel.readInt();
+ for (int i = 0; i < numberOfControls; i++) {
+ call.addControl(parcel.readInt());
+ }
+ return call;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int parcelableFlags) {
+ parcel.writeLong(mId);
+ parcel.writeString(mCallerId);
+ parcel.writeBlob(mAppIcon);
+ parcel.writeString(mAppName);
+ parcel.writeString(mAppIdentifier);
+ parcel.writeInt(mStatus);
+ parcel.writeInt(mControls.size());
+ for (int control : mControls) {
+ parcel.writeInt(control);
+ }
+ }
+
+ void setId(long id) {
+ mId = id;
+ }
+
+ void setCallerId(String callerId) {
+ mCallerId = callerId;
+ }
+
+ void setAppIcon(byte[] appIcon) {
+ mAppIcon = appIcon;
+ }
+
+ void setAppName(String appName) {
+ mAppName = appName;
+ }
+
+ void setAppIdentifier(String appIdentifier) {
+ mAppIdentifier = appIdentifier;
+ }
+
+ void setStatus(int status) {
+ mStatus = status;
+ }
+
+ void addControl(int control) {
+ mControls.add(control);
+ }
+
+ long getId() {
+ return mId;
+ }
+
+ String getCallerId() {
+ return mCallerId;
+ }
+
+ byte[] getAppIcon() {
+ return mAppIcon;
+ }
+
+ String getAppName() {
+ return mAppName;
+ }
+
+ String getAppIdentifier() {
+ return mAppIdentifier;
+ }
+
+ int getStatus() {
+ return mStatus;
+ }
+
+ Set<Integer> getControls() {
+ return mControls;
+ }
+
+ boolean hasControl(int control) {
+ return mControls.contains(control);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof CallMetadataSyncData.Call) {
+ return ((Call) other).getId() == getId();
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull public static final Parcelable.Creator<Call> CREATOR = new Parcelable.Creator<>() {
+
+ @Override
+ public Call createFromParcel(Parcel source) {
+ return Call.fromParcel(source);
+ }
+
+ @Override
+ public Call[] newArray(int size) {
+ return new Call[size];
+ }
+ };
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
index 077fd2a1157d..dd0bbf2790ee 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java
@@ -39,6 +39,8 @@ public class CrossDeviceCall {
private static final String TAG = "CrossDeviceCall";
+ public static final String EXTRA_CALL_ID =
+ "com.android.companion.datatransfer.contextsync.extra.CALL_ID";
private static final int APP_ICON_BITMAP_DIMENSION = 256;
private static final AtomicLong sNextId = new AtomicLong(1);
@@ -47,6 +49,7 @@ public class CrossDeviceCall {
private final Call mCall;
@VisibleForTesting boolean mIsEnterprise;
@VisibleForTesting boolean mIsOtt;
+ private final String mCallingAppPackageName;
private String mCallingAppName;
private byte[] mCallingAppIcon;
private String mCallerDisplayName;
@@ -59,7 +62,7 @@ public class CrossDeviceCall {
CallAudioState callAudioState) {
mId = sNextId.getAndIncrement();
mCall = call;
- final String callingAppPackageName = call != null
+ mCallingAppPackageName = call != null
? call.getDetails().getAccountHandle().getComponentName().getPackageName() : null;
mIsOtt = call != null
&& (call.getDetails().getCallCapabilities() & Call.Details.PROPERTY_SELF_MANAGED)
@@ -69,13 +72,13 @@ public class CrossDeviceCall {
== Call.Details.PROPERTY_ENTERPRISE_CALL;
try {
final ApplicationInfo applicationInfo = packageManager
- .getApplicationInfo(callingAppPackageName,
+ .getApplicationInfo(mCallingAppPackageName,
PackageManager.ApplicationInfoFlags.of(0));
mCallingAppName = packageManager.getApplicationLabel(applicationInfo).toString();
mCallingAppIcon = renderDrawableToByteArray(
packageManager.getApplicationIcon(applicationInfo));
} catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "Could not get application info for package " + callingAppPackageName, e);
+ Slog.e(TAG, "Could not get application info for package " + mCallingAppPackageName, e);
}
mIsMuted = callAudioState != null && callAudioState.isMuted();
if (call != null) {
@@ -170,7 +173,8 @@ public class CrossDeviceCall {
}
}
- private int convertStateToStatus(int callState) {
+ /** Converts a Telecom call state to a Context Sync status. */
+ public static int convertStateToStatus(int callState) {
switch (callState) {
case Call.STATE_HOLDING:
return android.companion.Telecom.Call.ON_HOLD;
@@ -178,20 +182,30 @@ public class CrossDeviceCall {
return android.companion.Telecom.Call.ONGOING;
case Call.STATE_RINGING:
return android.companion.Telecom.Call.RINGING;
- case Call.STATE_NEW:
- case Call.STATE_DIALING:
- case Call.STATE_DISCONNECTED:
- case Call.STATE_SELECT_PHONE_ACCOUNT:
- case Call.STATE_CONNECTING:
- case Call.STATE_DISCONNECTING:
- case Call.STATE_PULLING_CALL:
- case Call.STATE_AUDIO_PROCESSING:
- case Call.STATE_SIMULATED_RINGING:
default:
return android.companion.Telecom.Call.UNKNOWN_STATUS;
}
}
+ /**
+ * Converts a Context Sync status to a Telecom call state. Note that this is lossy for
+ * and RINGING_SILENCED, as Telecom does not distinguish between RINGING and RINGING_SILENCED.
+ */
+ public static int convertStatusToState(int status) {
+ switch (status) {
+ case android.companion.Telecom.Call.ON_HOLD:
+ return Call.STATE_HOLDING;
+ case android.companion.Telecom.Call.ONGOING:
+ return Call.STATE_ACTIVE;
+ case android.companion.Telecom.Call.RINGING:
+ case android.companion.Telecom.Call.RINGING_SILENCED:
+ return Call.STATE_RINGING;
+ case android.companion.Telecom.Call.UNKNOWN_STATUS:
+ default:
+ return Call.STATE_NEW;
+ }
+ }
+
public long getId() {
return mId;
}
@@ -208,6 +222,10 @@ public class CrossDeviceCall {
return mCallingAppIcon;
}
+ public String getCallingAppPackageName() {
+ return mCallingAppPackageName;
+ }
+
/**
* Get a human-readable "caller id" to display as the origin of the call.
*
diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
index 9677b70f4916..a3e095e4475b 100644
--- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
+++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
@@ -96,27 +96,29 @@ public class CompanionTransportManager {
/**
* Add a listener to receive callbacks when a message is received for the message type
*/
- @GuardedBy("mTransports")
public void addListener(int message, @NonNull IOnMessageReceivedListener listener) {
mMessageListeners.put(message, listener);
- for (int i = 0; i < mTransports.size(); i++) {
- mTransports.valueAt(i).addListener(message, listener);
+ synchronized (mTransports) {
+ for (int i = 0; i < mTransports.size(); i++) {
+ mTransports.valueAt(i).addListener(message, listener);
+ }
}
}
/**
* Add a listener to receive callbacks when any of the transports is changed
*/
- @GuardedBy("mTransports")
public void addListener(IOnTransportsChangedListener listener) {
Slog.i(TAG, "Registering OnTransportsChangedListener");
mTransportsListeners.register(listener);
List<AssociationInfo> associations = new ArrayList<>();
- for (int i = 0; i < mTransports.size(); i++) {
- AssociationInfo association = mAssociationStore.getAssociationById(
- mTransports.keyAt(i));
- if (association != null) {
- associations.add(association);
+ synchronized (mTransports) {
+ for (int i = 0; i < mTransports.size(); i++) {
+ AssociationInfo association = mAssociationStore.getAssociationById(
+ mTransports.keyAt(i));
+ if (association != null) {
+ associations.add(association);
+ }
}
}
mTransportsListeners.broadcast(listener1 -> {
@@ -148,18 +150,19 @@ public class CompanionTransportManager {
/**
* Send a message to remote devices through the transports
*/
- @GuardedBy("mTransports")
public void sendMessage(int message, byte[] data, int[] associationIds) {
Slog.i(TAG, "Sending message 0x" + Integer.toHexString(message)
+ " data length " + data.length);
- for (int i = 0; i < associationIds.length; i++) {
- if (mTransports.contains(associationIds[i])) {
- try {
- mTransports.get(associationIds[i]).sendMessage(message, data);
- } catch (IOException e) {
- Slog.e(TAG, "Failed to send message 0x" + Integer.toHexString(message)
- + " data length " + data.length + " to association "
- + associationIds[i]);
+ synchronized (mTransports) {
+ for (int i = 0; i < associationIds.length; i++) {
+ if (mTransports.contains(associationIds[i])) {
+ try {
+ mTransports.get(associationIds[i]).sendMessage(message, data);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to send message 0x" + Integer.toHexString(message)
+ + " data length " + data.length + " to association "
+ + associationIds[i]);
+ }
}
}
}
@@ -215,14 +218,15 @@ public class CompanionTransportManager {
}
}
- @GuardedBy("mTransports")
private void notifyOnTransportsChanged() {
List<AssociationInfo> associations = new ArrayList<>();
- for (int i = 0; i < mTransports.size(); i++) {
- AssociationInfo association = mAssociationStore.getAssociationById(
- mTransports.keyAt(i));
- if (association != null) {
- associations.add(association);
+ synchronized (mTransports) {
+ for (int i = 0; i < mTransports.size(); i++) {
+ AssociationInfo association = mAssociationStore.getAssociationById(
+ mTransports.keyAt(i));
+ if (association != null) {
+ associations.add(association);
+ }
}
}
mTransportsListeners.broadcast(listener -> {
@@ -233,14 +237,15 @@ public class CompanionTransportManager {
});
}
- @GuardedBy("mTransports")
private void initializeTransport(int associationId, ParcelFileDescriptor fd) {
Slog.i(TAG, "Initializing transport");
if (!isSecureTransportEnabled()) {
Transport transport = new RawTransport(associationId, fd, mContext);
addMessageListenersToTransport(transport);
transport.start();
- mTransports.put(associationId, transport);
+ synchronized (mTransports) {
+ mTransports.put(associationId, transport);
+ }
Slog.i(TAG, "RawTransport is created");
return;
}
@@ -283,7 +288,6 @@ public class CompanionTransportManager {
/**
* Depending on the remote platform info to decide which transport should be created
*/
- @GuardedBy("CompanionTransportManager.this.mTransports")
private void onPlatformInfoReceived(int associationId, byte[] data) {
if (mTempTransport.getAssociationId() != associationId) {
return;
@@ -330,7 +334,9 @@ public class CompanionTransportManager {
}
addMessageListenersToTransport(transport);
transport.start();
- mTransports.put(transport.getAssociationId(), transport);
+ synchronized (mTransports) {
+ mTransports.put(transport.getAssociationId(), transport);
+ }
// Doesn't need to notifyTransportsChanged here, it'll be done in attachSystemDataTransport
}
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 1a0588e999e2..307f7bfc1fd5 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -367,7 +367,7 @@ class InputController {
"Could not send key event to input device for given token");
}
return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getNativePointer(),
- event.getKeyCode(), event.getAction());
+ event.getKeyCode(), event.getAction(), event.getEventTimeNanos());
}
}
@@ -380,7 +380,7 @@ class InputController {
"Could not send key event to input device for given token");
}
return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getNativePointer(),
- event.getKeyCode(), event.getAction());
+ event.getKeyCode(), event.getAction(), event.getEventTimeNanos());
}
}
@@ -398,7 +398,7 @@ class InputController {
"Display id associated with this mouse is not currently targetable");
}
return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getNativePointer(),
- event.getButtonCode(), event.getAction());
+ event.getButtonCode(), event.getAction(), event.getEventTimeNanos());
}
}
@@ -412,7 +412,8 @@ class InputController {
}
return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getNativePointer(),
event.getPointerId(), event.getToolType(), event.getAction(), event.getX(),
- event.getY(), event.getPressure(), event.getMajorAxisSize());
+ event.getY(), event.getPressure(), event.getMajorAxisSize(),
+ event.getEventTimeNanos());
}
}
@@ -430,7 +431,7 @@ class InputController {
"Display id associated with this mouse is not currently targetable");
}
return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getNativePointer(),
- event.getRelativeX(), event.getRelativeY());
+ event.getRelativeX(), event.getRelativeY(), event.getEventTimeNanos());
}
}
@@ -448,7 +449,7 @@ class InputController {
"Display id associated with this mouse is not currently targetable");
}
return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getNativePointer(),
- event.getXAxisMovement(), event.getYAxisMovement());
+ event.getXAxisMovement(), event.getYAxisMovement(), event.getEventTimeNanos());
}
}
@@ -514,15 +515,19 @@ class InputController {
private static native long nativeOpenUinputTouchscreen(String deviceName, int vendorId,
int productId, String phys, int height, int width);
private static native void nativeCloseUinput(long ptr);
- private static native boolean nativeWriteDpadKeyEvent(long ptr, int androidKeyCode, int action);
- private static native boolean nativeWriteKeyEvent(long ptr, int androidKeyCode, int action);
- private static native boolean nativeWriteButtonEvent(long ptr, int buttonCode, int action);
+ private static native boolean nativeWriteDpadKeyEvent(long ptr, int androidKeyCode, int action,
+ long eventTimeNanos);
+ private static native boolean nativeWriteKeyEvent(long ptr, int androidKeyCode, int action,
+ long eventTimeNanos);
+ private static native boolean nativeWriteButtonEvent(long ptr, int buttonCode, int action,
+ long eventTimeNanos);
private static native boolean nativeWriteTouchEvent(long ptr, int pointerId, int toolType,
- int action, float locationX, float locationY, float pressure, float majorAxisSize);
+ int action, float locationX, float locationY, float pressure, float majorAxisSize,
+ long eventTimeNanos);
private static native boolean nativeWriteRelativeEvent(long ptr, float relativeX,
- float relativeY);
+ float relativeY, long eventTimeNanos);
private static native boolean nativeWriteScrollEvent(long ptr, float xAxisMovement,
- float yAxisMovement);
+ float yAxisMovement, long eventTimeNanos);
/** Wrapper around the static native methods for tests. */
@VisibleForTesting
@@ -550,32 +555,37 @@ class InputController {
nativeCloseUinput(ptr);
}
- public boolean writeDpadKeyEvent(long ptr, int androidKeyCode, int action) {
- return nativeWriteDpadKeyEvent(ptr, androidKeyCode, action);
+ public boolean writeDpadKeyEvent(long ptr, int androidKeyCode, int action,
+ long eventTimeNanos) {
+ return nativeWriteDpadKeyEvent(ptr, androidKeyCode, action, eventTimeNanos);
}
- public boolean writeKeyEvent(long ptr, int androidKeyCode, int action) {
- return nativeWriteKeyEvent(ptr, androidKeyCode, action);
+ public boolean writeKeyEvent(long ptr, int androidKeyCode, int action,
+ long eventTimeNanos) {
+ return nativeWriteKeyEvent(ptr, androidKeyCode, action, eventTimeNanos);
}
- public boolean writeButtonEvent(long ptr, int buttonCode, int action) {
- return nativeWriteButtonEvent(ptr, buttonCode, action);
+ public boolean writeButtonEvent(long ptr, int buttonCode, int action,
+ long eventTimeNanos) {
+ return nativeWriteButtonEvent(ptr, buttonCode, action, eventTimeNanos);
}
public boolean writeTouchEvent(long ptr, int pointerId, int toolType, int action,
- float locationX, float locationY, float pressure, float majorAxisSize) {
+ float locationX, float locationY, float pressure, float majorAxisSize,
+ long eventTimeNanos) {
return nativeWriteTouchEvent(ptr, pointerId, toolType,
action, locationX, locationY,
- pressure, majorAxisSize);
+ pressure, majorAxisSize, eventTimeNanos);
}
- public boolean writeRelativeEvent(long ptr, float relativeX, float relativeY) {
- return nativeWriteRelativeEvent(ptr, relativeX, relativeY);
+ public boolean writeRelativeEvent(long ptr, float relativeX, float relativeY,
+ long eventTimeNanos) {
+ return nativeWriteRelativeEvent(ptr, relativeX, relativeY, eventTimeNanos);
}
- public boolean writeScrollEvent(long ptr, float xAxisMovement, float yAxisMovement) {
- return nativeWriteScrollEvent(ptr, xAxisMovement,
- yAxisMovement);
+ public boolean writeScrollEvent(long ptr, float xAxisMovement, float yAxisMovement,
+ long eventTimeNanos) {
+ return nativeWriteScrollEvent(ptr, xAxisMovement, yAxisMovement, eventTimeNanos);
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index ae88f24ab409..de0f68ccd665 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -404,39 +404,44 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
public void close() {
super.close_enforcePermission();
// Remove about-to-be-closed virtual device from the service before butchering it.
- mService.removeVirtualDevice(mDeviceId);
+ boolean removed = mService.removeVirtualDevice(mDeviceId);
mDeviceId = Context.DEVICE_ID_INVALID;
- VirtualDisplayWrapper[] virtualDisplaysToBeReleased;
- synchronized (mVirtualDeviceLock) {
- if (mVirtualAudioController != null) {
- mVirtualAudioController.stopListening();
- mVirtualAudioController = null;
- }
- mLocaleList = null;
- virtualDisplaysToBeReleased = new VirtualDisplayWrapper[mVirtualDisplays.size()];
- for (int i = 0; i < mVirtualDisplays.size(); i++) {
- virtualDisplaysToBeReleased[i] = mVirtualDisplays.valueAt(i);
- }
- mVirtualDisplays.clear();
- mVirtualSensorList = null;
- mVirtualSensors.clear();
- }
- // Destroy the display outside locked section.
- for (VirtualDisplayWrapper virtualDisplayWrapper : virtualDisplaysToBeReleased) {
- mDisplayManager.releaseVirtualDisplay(virtualDisplayWrapper.getToken());
- // The releaseVirtualDisplay call above won't trigger
- // VirtualDeviceImpl.onVirtualDisplayRemoved callback because we already removed the
- // virtual device from the service - we release the other display-tied resources here
- // with the guarantee it will be done exactly once.
- releaseOwnedVirtualDisplayResources(virtualDisplayWrapper);
+ // Device is already closed.
+ if (!removed) {
+ return;
}
- mAppToken.unlinkToDeath(this, 0);
- mCameraAccessController.stopObservingIfNeeded();
-
final long ident = Binder.clearCallingIdentity();
try {
+ VirtualDisplayWrapper[] virtualDisplaysToBeReleased;
+ synchronized (mVirtualDeviceLock) {
+ if (mVirtualAudioController != null) {
+ mVirtualAudioController.stopListening();
+ mVirtualAudioController = null;
+ }
+ mLocaleList = null;
+ virtualDisplaysToBeReleased = new VirtualDisplayWrapper[mVirtualDisplays.size()];
+ for (int i = 0; i < mVirtualDisplays.size(); i++) {
+ virtualDisplaysToBeReleased[i] = mVirtualDisplays.valueAt(i);
+ }
+ mVirtualDisplays.clear();
+ mVirtualSensorList = null;
+ mVirtualSensors.clear();
+ }
+ // Destroy the display outside locked section.
+ for (VirtualDisplayWrapper virtualDisplayWrapper : virtualDisplaysToBeReleased) {
+ mDisplayManager.releaseVirtualDisplay(virtualDisplayWrapper.getToken());
+ // The releaseVirtualDisplay call above won't trigger
+ // VirtualDeviceImpl.onVirtualDisplayRemoved callback because we already removed the
+ // virtual device from the service - we release the other display-tied resources
+ // here with the guarantee it will be done exactly once.
+ releaseOwnedVirtualDisplayResources(virtualDisplayWrapper);
+ }
+
+ mAppToken.unlinkToDeath(this, 0);
+ mCameraAccessController.stopObservingIfNeeded();
+
mInputController.close();
mSensorController.close();
} finally {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 96446422dd85..ad4c0bf26d62 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -202,8 +202,19 @@ public class VirtualDeviceManagerService extends SystemService {
}
}
- void removeVirtualDevice(int deviceId) {
+ /**
+ * Remove the virtual device. Sends the
+ * {@link VirtualDeviceManager#ACTION_VIRTUAL_DEVICE_REMOVED} broadcast as a result.
+ *
+ * @param deviceId deviceId to be removed
+ * @return {@code true} if the device was removed, {@code false} if the operation was a no-op
+ */
+ boolean removeVirtualDevice(int deviceId) {
synchronized (mVirtualDeviceManagerLock) {
+ if (!mVirtualDevices.contains(deviceId)) {
+ return false;
+ }
+
mAppsOnVirtualDevices.remove(deviceId);
mVirtualDevices.remove(deviceId);
}
@@ -223,6 +234,7 @@ public class VirtualDeviceManagerService extends SystemService {
} finally {
Binder.restoreCallingIdentity(identity);
}
+ return true;
}
private void syncVirtualDevicesToCdmAssociations(List<AssociationInfo> associations) {
@@ -248,7 +260,6 @@ public class VirtualDeviceManagerService extends SystemService {
for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) {
virtualDevice.close();
}
-
}
private void registerCdmAssociationListener() {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index cfdf3ac5915b..f8d19ec9903d 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -161,6 +161,7 @@ java_library_static {
"android.hardware.health-V2-java", // AIDL
"android.hardware.health-translate-java",
"android.hardware.light-V1-java",
+ "android.hardware.security.rkp-V3-java",
"android.hardware.tv.cec-V1.1-java",
"android.hardware.tv.hdmi.cec-V1-java",
"android.hardware.tv.hdmi.connection-V1-java",
@@ -177,6 +178,7 @@ java_library_static {
"android.hardware.power.stats-V2-java",
"android.hardware.power-V4-java",
"android.hidl.manager-V1.2-java",
+ "cbor-java",
"icu4j_calendar_astronomer",
"netd-client",
"overlayable_policy_aidl-java",
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 12ee13183221..0713999d4354 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -17,7 +17,6 @@
package android.os;
import android.annotation.IntDef;
-import android.annotation.NonNull;
import android.net.Network;
import com.android.internal.os.BinderCallsStats;
@@ -40,6 +39,8 @@ public abstract class BatteryStatsInternal {
public static final int CPU_WAKEUP_SUBSYSTEM_ALARM = 1;
public static final int CPU_WAKEUP_SUBSYSTEM_WIFI = 2;
public static final int CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER = 3;
+ public static final int CPU_WAKEUP_SUBSYSTEM_SENSOR = 4;
+ public static final int CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA = 5;
/** @hide */
@IntDef(prefix = {"CPU_WAKEUP_SUBSYSTEM_"}, value = {
@@ -47,9 +48,11 @@ public abstract class BatteryStatsInternal {
CPU_WAKEUP_SUBSYSTEM_ALARM,
CPU_WAKEUP_SUBSYSTEM_WIFI,
CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
+ CPU_WAKEUP_SUBSYSTEM_SENSOR,
+ CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA,
})
@Retention(RetentionPolicy.SOURCE)
- @interface CpuWakeupSubsystem {
+ public @interface CpuWakeupSubsystem {
}
/**
@@ -107,19 +110,16 @@ public abstract class BatteryStatsInternal {
public abstract void noteBinderThreadNativeIds(int[] binderThreadNativeTids);
/**
- * Reports any activity that could potentially have caused the CPU to wake up.
- * Accepts a timestamp to allow free ordering between the event and its reporting.
- * @param subsystem The subsystem this activity should be attributed to.
- * @param elapsedMillis The time when this activity happened in the elapsed timebase.
- * @param uids The uid (or uids) that should be blamed for this activity.
- */
- public abstract void noteCpuWakingActivity(@CpuWakeupSubsystem int subsystem,
- long elapsedMillis, @NonNull int... uids);
-
- /**
* Reports a sound trigger recognition event that may have woken up the CPU.
* @param elapsedMillis The time when the event happened in the elapsed timebase.
* @param uid The uid that requested this trigger.
*/
public abstract void noteWakingSoundTrigger(long elapsedMillis, int uid);
+
+ /**
+ * Reports an alarm batch that would have woken up the CPU.
+ * @param elapsedMillis The time at which this alarm batch was scheduled to go off.
+ * @param uids the uids of all apps that have any alarm in this batch.
+ */
+ public abstract void noteWakingAlarmBatch(long elapsedMillis, int... uids);
}
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index d256aead97e8..f4f5c951faaa 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -580,6 +580,7 @@ public class PackageWatchdog {
PackageHealthObserverImpact.USER_IMPACT_LEVEL_10,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_50,
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_60,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100})
public @interface PackageHealthObserverImpact {
@@ -590,6 +591,7 @@ public class PackageWatchdog {
/* Actions having medium user impact, user of a device will likely notice. */
int USER_IMPACT_LEVEL_30 = 30;
int USER_IMPACT_LEVEL_50 = 50;
+ int USER_IMPACT_LEVEL_60 = 60;
int USER_IMPACT_LEVEL_70 = 70;
/* Action has high user impact, a last resort, user of a device will be very frustrated. */
int USER_IMPACT_LEVEL_100 = 100;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1f80aec3d443..def2a2f150b7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16707,6 +16707,11 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.println(String.format("Resources History for %s (%s)",
app.processName,
app.info.packageName));
+ if (app.mOptRecord.isFrozen()) {
+ pw.println(" Skipping frozen process");
+ pw.flush();
+ continue;
+ }
pw.flush();
try {
TransferPipe tp = new TransferPipe(" ");
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c0b3a90d923b..d140403f77c3 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -23,6 +23,7 @@ import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.POWER_SAVER;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
@@ -51,6 +52,7 @@ import android.os.BatteryConsumer;
import android.os.BatteryManagerInternal;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
+import android.os.BatteryStatsInternal.CpuWakeupSubsystem;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Binder;
@@ -474,6 +476,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
private int transportToSubsystem(NetworkCapabilities nc) {
if (nc.hasTransport(TRANSPORT_WIFI)) {
return CPU_WAKEUP_SUBSYSTEM_WIFI;
+ } else if (nc.hasTransport(TRANSPORT_CELLULAR)) {
+ return CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA;
}
return CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
}
@@ -514,14 +518,32 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
@Override
- public void noteCpuWakingActivity(int subsystem, long elapsedMillis, int... uids) {
- Objects.requireNonNull(uids);
- mHandler.post(() -> mCpuWakeupStats.noteWakingActivity(subsystem, elapsedMillis, uids));
- }
- @Override
public void noteWakingSoundTrigger(long elapsedMillis, int uid) {
noteCpuWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER, elapsedMillis, uid);
}
+
+ @Override
+ public void noteWakingAlarmBatch(long elapsedMillis, int... uids) {
+ noteCpuWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, elapsedMillis, uids);
+ }
+ }
+
+ /**
+ * Reports any activity that could potentially have caused the CPU to wake up.
+ * Accepts a timestamp to allow free ordering between the event and its reporting.
+ *
+ * <p>
+ * This method can be called multiple times for the same wakeup and then all attribution
+ * reported will be unioned as long as all reports are made within a small amount of cpu uptime
+ * after the wakeup is reported to batterystats.
+ *
+ * @param subsystem The subsystem this activity should be attributed to.
+ * @param elapsedMillis The time when this activity happened in the elapsed timebase.
+ * @param uids The uid (or uids) that should be blamed for this activity.
+ */
+ void noteCpuWakingActivity(@CpuWakeupSubsystem int subsystem, long elapsedMillis, int... uids) {
+ Objects.requireNonNull(uids);
+ mHandler.post(() -> mCpuWakeupStats.noteWakingActivity(subsystem, elapsedMillis, uids));
}
@Override
@@ -1267,6 +1289,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
if (callingUid != Process.SYSTEM_UID) {
throw new SecurityException("Calling uid " + callingUid + " is not system uid");
}
+ final long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos);
final SensorManager sm = mContext.getSystemService(SensorManager.class);
final Sensor sensor = sm.getSensorByHandle(sensorHandle);
@@ -1275,10 +1298,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub
+ " received in noteWakeupSensorEvent");
return;
}
- Slog.i(TAG, "Sensor " + sensor + " wakeup event at " + elapsedNanos + " sent to uid "
- + uid);
- // TODO (b/275436924): Remove log and pipe to CpuWakeupStats for wakeup attribution
- // This method should return as quickly as possible. Use mHandler#post to do longer work.
+ if (uid < 0) {
+ Slog.wtf(TAG, "Invalid uid " + uid + " for sensor event with sensor: " + sensor);
+ return;
+ }
+ // TODO (b/278319756): Also pipe in Sensor type for more usefulness.
+ noteCpuWakingActivity(BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SENSOR, elapsedMillis, uid);
}
@Override
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index d926c2c7c7a8..a181402e2d9f 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -141,6 +141,7 @@ import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
/**
* Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
@@ -157,7 +158,7 @@ class UserController implements Handler.Callback {
private static final String TAG = TAG_WITH_CLASS_NAME ? "UserController" : TAG_AM;
// Amount of time we wait for observers to handle a user switch before
- // giving up on them and unfreezing the screen.
+ // giving up on them and dismissing the user switching dialog.
static final int DEFAULT_USER_SWITCH_TIMEOUT_MS = 3 * 1000;
/**
@@ -207,7 +208,7 @@ class UserController implements Handler.Callback {
/**
* Amount of time waited for {@link WindowManagerService#dismissKeyguard} callbacks to be
* called after dismissing the keyguard.
- * Otherwise, we should move on to unfreeze the screen {@link #unfreezeScreen}
+ * Otherwise, we should move on to dismiss the dialog {@link #dismissUserSwitchDialog()}
* and report user switch is complete {@link #REPORT_USER_SWITCH_COMPLETE_MSG}.
*/
private static final int DISMISS_KEYGUARD_TIMEOUT_MS = 2 * 1000;
@@ -1695,14 +1696,6 @@ class UserController implements Handler.Callback {
return false;
}
- if (foreground && isUserSwitchUiEnabled()) {
- t.traceBegin("startFreezingScreen");
- mInjector.getWindowManager().startFreezingScreen(
- R.anim.screen_user_exit, R.anim.screen_user_enter);
- t.traceEnd();
- }
- dismissUserSwitchDialog(); // so that we don't hold a reference to mUserSwitchingDialog
-
boolean needStart = false;
boolean updateUmState = false;
UserState uss;
@@ -1877,7 +1870,7 @@ class UserController implements Handler.Callback {
if (!success) {
mInjector.getWindowManager().setSwitchingUser(false);
mTargetUserId = UserHandle.USER_NULL;
- dismissUserSwitchDialog();
+ dismissUserSwitchDialog(null);
}
}
@@ -2015,22 +2008,26 @@ class UserController implements Handler.Callback {
mUiHandler.sendMessage(mUiHandler.obtainMessage(
START_USER_SWITCH_UI_MSG, userNames));
} else {
- mHandler.removeMessages(START_USER_SWITCH_FG_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(
- START_USER_SWITCH_FG_MSG, targetUserId, 0));
+ sendStartUserSwitchFgMessage(targetUserId);
}
return true;
}
- private void dismissUserSwitchDialog() {
- mInjector.dismissUserSwitchingDialog();
+ private void sendStartUserSwitchFgMessage(int targetUserId) {
+ mHandler.removeMessages(START_USER_SWITCH_FG_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(START_USER_SWITCH_FG_MSG, targetUserId, 0));
+ }
+
+ private void dismissUserSwitchDialog(Runnable onDismissed) {
+ mInjector.dismissUserSwitchingDialog(onDismissed);
}
private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
// The dialog will show and then initiate the user switch by calling startUserInForeground
mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second,
getSwitchingFromSystemUserMessageUnchecked(),
- getSwitchingToSystemUserMessageUnchecked());
+ getSwitchingToSystemUserMessageUnchecked(),
+ /* onShown= */ () -> sendStartUserSwitchFgMessage(fromToUserPair.second.id));
}
private void dispatchForegroundProfileChanged(@UserIdInt int userId) {
@@ -2236,7 +2233,7 @@ class UserController implements Handler.Callback {
EventLog.writeEvent(EventLogTags.UC_CONTINUE_USER_SWITCH, oldUserId, newUserId);
- // Do the keyguard dismiss and unfreeze later
+ // Do the keyguard dismiss and dismiss the user switching dialog later
mHandler.removeMessages(COMPLETE_USER_SWITCH_MSG);
mHandler.sendMessage(mHandler.obtainMessage(
COMPLETE_USER_SWITCH_MSG, oldUserId, newUserId));
@@ -2251,37 +2248,33 @@ class UserController implements Handler.Callback {
@VisibleForTesting
void completeUserSwitch(int oldUserId, int newUserId) {
final boolean isUserSwitchUiEnabled = isUserSwitchUiEnabled();
- final Runnable runnable = () -> {
- if (isUserSwitchUiEnabled) {
- unfreezeScreen();
- }
- mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(
- REPORT_USER_SWITCH_COMPLETE_MSG, oldUserId, newUserId));
- };
+ // serialize each conditional step
+ await(
+ // STEP 1 - If there is no challenge set, dismiss the keyguard right away
+ isUserSwitchUiEnabled && !mInjector.getKeyguardManager().isDeviceSecure(newUserId),
+ mInjector::dismissKeyguard,
+ () -> await(
+ // STEP 2 - If user switch ui was enabled, dismiss user switch dialog
+ isUserSwitchUiEnabled,
+ this::dismissUserSwitchDialog,
+ () -> {
+ // STEP 3 - Send REPORT_USER_SWITCH_COMPLETE_MSG to broadcast
+ // ACTION_USER_SWITCHED & call UserSwitchObservers.onUserSwitchComplete
+ mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
+ mHandler.sendMessage(mHandler.obtainMessage(
+ REPORT_USER_SWITCH_COMPLETE_MSG, oldUserId, newUserId));
+ }
+ ));
+ }
- // If there is no challenge set, dismiss the keyguard right away
- if (isUserSwitchUiEnabled && !mInjector.getKeyguardManager().isDeviceSecure(newUserId)) {
- // Wait until the keyguard is dismissed to unfreeze
- mInjector.dismissKeyguard(runnable);
+ private void await(boolean condition, Consumer<Runnable> conditionalStep, Runnable nextStep) {
+ if (condition) {
+ conditionalStep.accept(nextStep);
} else {
- runnable.run();
+ nextStep.run();
}
}
- /**
- * Tell WindowManager we're ready to unfreeze the screen, at its leisure. Note that there is
- * likely a lot going on, and WM won't unfreeze until the drawing is all done, so
- * the actual unfreeze may still not happen for a long time; this is expected.
- */
- @VisibleForTesting
- void unfreezeScreen() {
- TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- t.traceBegin("stopFreezingScreen");
- mInjector.getWindowManager().stopFreezingScreen();
- t.traceEnd();
- }
-
private void moveUserToForeground(UserState uss, int newUserId) {
boolean homeInFront = mInjector.taskSupervisorSwitchUser(newUserId, uss);
if (homeInFront) {
@@ -3731,17 +3724,18 @@ class UserController implements Handler.Callback {
mService.mCpHelper.installEncryptionUnawareProviders(userId);
}
- void dismissUserSwitchingDialog() {
+ void dismissUserSwitchingDialog(@Nullable Runnable onDismissed) {
synchronized (mUserSwitchingDialogLock) {
if (mUserSwitchingDialog != null) {
- mUserSwitchingDialog.dismiss();
+ mUserSwitchingDialog.dismiss(onDismissed);
mUserSwitchingDialog = null;
}
}
}
void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser,
- String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
+ String switchingFromSystemUserMessage, String switchingToSystemUserMessage,
+ @NonNull Runnable onShown) {
if (mService.mContext.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
// config_customUserSwitchUi is set to true on Automotive as CarSystemUI is
@@ -3751,11 +3745,10 @@ class UserController implements Handler.Callback {
+ "condition if it's shown by CarSystemUI as well");
}
synchronized (mUserSwitchingDialogLock) {
- dismissUserSwitchingDialog();
- mUserSwitchingDialog = new UserSwitchingDialog(mService, mService.mContext,
- fromUser, toUser, true /* above system */, switchingFromSystemUserMessage,
- switchingToSystemUserMessage);
- mUserSwitchingDialog.show();
+ dismissUserSwitchingDialog(null);
+ mUserSwitchingDialog = new UserSwitchingDialog(mService.mContext, fromUser, toUser,
+ switchingFromSystemUserMessage, switchingToSystemUserMessage);
+ mUserSwitchingDialog.show(onShown);
}
}
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index a5651bfa3dde..649305f7ee01 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -16,160 +16,258 @@
package com.android.server.am;
-import android.app.AlertDialog;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.Dialog;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.Resources;
-import android.os.Handler;
-import android.os.Message;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Animatable2;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
-import android.view.LayoutInflater;
+import android.util.TypedValue;
import android.view.View;
-import android.view.ViewTreeObserver;
+import android.view.Window;
import android.view.WindowManager;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ObjectUtils;
+import com.android.internal.util.UserIcons;
/**
- * Dialog to show when a user switch it about to happen. The intent is to snapshot the screen
- * immediately after the dialog shows so that the user is informed that something is happening
- * in the background rather than just freeze the screen and not know if the user-switch affordance
- * was being handled.
+ * Dialog to show during the user switch. This dialog shows target user's name and their profile
+ * picture with a circular spinner animation around it if the animations for this dialog are not
+ * disabled. And covers the whole screen so that all the UI jank caused by the switch are hidden.
*/
-class UserSwitchingDialog extends AlertDialog
- implements ViewTreeObserver.OnWindowShownListener {
- private static final String TAG = "ActivityManagerUserSwitchingDialog";
-
- // Time to wait for the onWindowShown() callback before continuing the user switch
- private static final int WINDOW_SHOWN_TIMEOUT_MS = 3000;
+class UserSwitchingDialog extends Dialog {
+ private static final String TAG = "UserSwitchingDialog";
+ private static final long TRACE_TAG = Trace.TRACE_TAG_ACTIVITY_MANAGER;
// User switching doesn't happen that frequently, so it doesn't hurt to have it always on
protected static final boolean DEBUG = true;
+ private static final long DIALOG_SHOW_HIDE_ANIMATION_DURATION_MS = 300;
+ private final boolean mDisableAnimations;
+
+ protected final UserInfo mOldUser;
+ protected final UserInfo mNewUser;
+ private final String mSwitchingFromSystemUserMessage;
+ private final String mSwitchingToSystemUserMessage;
+ protected final Context mContext;
+ private final int mTraceCookie;
- private final ActivityManagerService mService;
- private final int mUserId;
- private static final int MSG_START_USER = 1;
- @GuardedBy("this")
- private boolean mStartedUser;
- final protected UserInfo mOldUser;
- final protected UserInfo mNewUser;
- final private String mSwitchingFromSystemUserMessage;
- final private String mSwitchingToSystemUserMessage;
- final protected Context mContext;
-
- public UserSwitchingDialog(ActivityManagerService service, Context context, UserInfo oldUser,
- UserInfo newUser, boolean aboveSystem, String switchingFromSystemUserMessage,
- String switchingToSystemUserMessage) {
- super(context);
+ UserSwitchingDialog(Context context, UserInfo oldUser, UserInfo newUser,
+ String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
+ // TODO(b/278857848): Make full screen user switcher cover top part of the screen as well.
+ // This problem is seen only on phones, it works fine on tablets.
+ super(context, R.style.Theme_Material_NoActionBar_Fullscreen);
mContext = context;
- mService = service;
- mUserId = newUser.id;
mOldUser = oldUser;
mNewUser = newUser;
mSwitchingFromSystemUserMessage = switchingFromSystemUserMessage;
mSwitchingToSystemUserMessage = switchingToSystemUserMessage;
+ mDisableAnimations = ActivityManager.isLowRamDeviceStatic() || SystemProperties.getBoolean(
+ "debug.usercontroller.disable_user_switching_dialog_animations", false);
+ mTraceCookie = UserHandle.MAX_SECONDARY_USER_ID * oldUser.id + newUser.id;
inflateContent();
+ configureWindow();
+ }
- if (aboveSystem) {
- getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
- }
-
- WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ private void configureWindow() {
+ final Window window = getWindow();
+ final WindowManager.LayoutParams attrs = window.getAttributes();
attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR |
- WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- getWindow().setAttributes(attrs);
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ window.setAttributes(attrs);
+ window.setBackgroundDrawableResource(android.R.color.transparent);
+ window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
}
void inflateContent() {
- // Set up the dialog contents
setCancelable(false);
- Resources res = getContext().getResources();
- // Custom view due to alignment and font size requirements
- TextView view = (TextView) LayoutInflater.from(getContext()).inflate(
- R.layout.user_switching_dialog, null);
+ setContentView(R.layout.user_switching_dialog);
- String viewMessage = null;
- if (UserManager.isDeviceInDemoMode(mContext)) {
- if (mOldUser.isDemo()) {
- viewMessage = res.getString(R.string.demo_restarting_message);
+ final TextView textView = findViewById(R.id.message);
+ if (textView != null) {
+ final String message = getTextMessage();
+ textView.setAccessibilityPaneTitle(message);
+ textView.setText(message);
+ }
+
+ final ImageView imageView = findViewById(R.id.icon);
+ if (imageView != null) {
+ imageView.setImageBitmap(getUserIconRounded());
+ }
+
+ final ImageView progressCircular = findViewById(R.id.progress_circular);
+ if (progressCircular != null) {
+ if (mDisableAnimations) {
+ progressCircular.setVisibility(View.GONE);
} else {
- viewMessage = res.getString(R.string.demo_starting_message);
- }
- } else {
- if (mOldUser.id == UserHandle.USER_SYSTEM) {
- viewMessage = mSwitchingFromSystemUserMessage;
- } else if (mNewUser.id == UserHandle.USER_SYSTEM) {
- viewMessage = mSwitchingToSystemUserMessage;
+ final TypedValue value = new TypedValue();
+ getContext().getTheme().resolveAttribute(R.attr.colorAccentPrimary, value, true);
+ progressCircular.setColorFilter(value.data);
}
+ }
+ }
- // If switchingFromSystemUserMessage or switchingToSystemUserMessage is null, fallback
- // to system message.
- if (viewMessage == null) {
- viewMessage = res.getString(R.string.user_switching_message, mNewUser.name);
- }
+ private Bitmap getUserIconRounded() {
+ final Bitmap bmp = ObjectUtils.getOrElse(BitmapFactory.decodeFile(mNewUser.iconPath),
+ defaultUserIcon(mNewUser.id));
+ final int w = bmp.getWidth();
+ final int h = bmp.getHeight();
+ final Bitmap bmpRounded = Bitmap.createBitmap(w, h, bmp.getConfig());
+ final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ paint.setShader(new BitmapShader(bmp, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
+ new Canvas(bmpRounded).drawRoundRect((new RectF(0, 0, w, h)), w / 2f, h / 2f, paint);
+ return bmpRounded;
+ }
+
+ private Bitmap defaultUserIcon(@UserIdInt int userId) {
+ final Resources res = getContext().getResources();
+ final Drawable icon = UserIcons.getDefaultUserIcon(res, userId, /* light= */ false);
+ return UserIcons.convertToBitmapAtUserIconSize(res, icon);
+ }
- view.setCompoundDrawablesWithIntrinsicBounds(null,
- getContext().getDrawable(R.drawable.ic_swap_horiz), null, null);
+ private String getTextMessage() {
+ final Resources res = getContext().getResources();
+
+ if (UserManager.isDeviceInDemoMode(mContext)) {
+ return res.getString(mOldUser.isDemo()
+ ? R.string.demo_restarting_message
+ : R.string.demo_starting_message);
}
- view.setAccessibilityPaneTitle(viewMessage);
- view.setText(viewMessage);
- setView(view);
+
+ final String message =
+ mOldUser.id == UserHandle.USER_SYSTEM ? mSwitchingFromSystemUserMessage
+ : mNewUser.id == UserHandle.USER_SYSTEM ? mSwitchingToSystemUserMessage : null;
+
+ return message != null ? message
+ // If switchingFromSystemUserMessage or switchingToSystemUserMessage is null,
+ // fallback to system message.
+ : res.getString(R.string.user_switching_message, mNewUser.name);
}
@Override
public void show() {
- if (DEBUG) Slog.d(TAG, "show called");
+ asyncTraceBegin("", 0);
super.show();
- final View decorView = getWindow().getDecorView();
- if (decorView != null) {
- decorView.getViewTreeObserver().addOnWindowShownListener(this);
- }
- // Add a timeout as a safeguard, in case a race in screen on/off causes the window
- // callback to never come.
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_USER),
- WINDOW_SHOWN_TIMEOUT_MS);
}
@Override
- public void onWindowShown() {
- if (DEBUG) Slog.d(TAG, "onWindowShown called");
- startUser();
+ public void dismiss() {
+ super.dismiss();
+ asyncTraceEnd("", 0);
+ }
+
+ public void show(@NonNull Runnable onShown) {
+ if (DEBUG) Slog.d(TAG, "show called");
+ show();
+
+ if (mDisableAnimations) {
+ onShown.run();
+ } else {
+ startShowAnimation(onShown);
+ }
}
- void startUser() {
- synchronized (this) {
- if (!mStartedUser) {
- Slog.i(TAG, "starting user " + mUserId);
- mService.mUserController.startUserInForeground(mUserId);
+ public void dismiss(@Nullable Runnable onDismissed) {
+ if (DEBUG) Slog.d(TAG, "dismiss called");
+
+ if (onDismissed == null) {
+ // no animation needed
+ dismiss();
+ } else if (mDisableAnimations) {
+ dismiss();
+ onDismissed.run();
+ } else {
+ startDismissAnimation(() -> {
dismiss();
- mStartedUser = true;
- final View decorView = getWindow().getDecorView();
- if (decorView != null) {
- decorView.getViewTreeObserver().removeOnWindowShownListener(this);
- }
- mHandler.removeMessages(MSG_START_USER);
- } else {
- Slog.i(TAG, "user " + mUserId + " already started");
- }
+ onDismissed.run();
+ });
}
}
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_START_USER:
- Slog.w(TAG, "user switch window not shown in "
- + WINDOW_SHOWN_TIMEOUT_MS + " ms");
- startUser();
- break;
+ private void startShowAnimation(Runnable onAnimationEnd) {
+ asyncTraceBegin("-showAnimation", 1);
+ startDialogAnimation(new AlphaAnimation(0, 1), () -> {
+ asyncTraceEnd("-showAnimation", 1);
+
+ asyncTraceBegin("-spinnerAnimation", 2);
+ startProgressAnimation(() -> {
+ asyncTraceEnd("-spinnerAnimation", 2);
+
+ onAnimationEnd.run();
+ });
+ });
+ }
+
+ private void startDismissAnimation(Runnable onAnimationEnd) {
+ asyncTraceBegin("-dismissAnimation", 3);
+ startDialogAnimation(new AlphaAnimation(1, 0), () -> {
+ asyncTraceEnd("-dismissAnimation", 3);
+
+ onAnimationEnd.run();
+ });
+ }
+
+ private void startProgressAnimation(Runnable onAnimationEnd) {
+ final ImageView progressCircular = findViewById(R.id.progress_circular);
+ final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) progressCircular.getDrawable();
+ avd.registerAnimationCallback(new Animatable2.AnimationCallback() {
+ @Override
+ public void onAnimationEnd(Drawable drawable) {
+ onAnimationEnd.run();
}
- }
- };
+ });
+ avd.start();
+ }
+
+ private void startDialogAnimation(Animation animation, Runnable onAnimationEnd) {
+ animation.setDuration(DIALOG_SHOW_HIDE_ANIMATION_DURATION_MS);
+ animation.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ onAnimationEnd.run();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+
+ }
+ });
+ findViewById(R.id.content).startAnimation(animation);
+ }
+
+ private void asyncTraceBegin(String subTag, int subCookie) {
+ Trace.asyncTraceBegin(TRACE_TAG, TAG + subTag, mTraceCookie + subCookie);
+ }
+
+ private void asyncTraceEnd(String subTag, int subCookie) {
+ Trace.asyncTraceEnd(TRACE_TAG, TAG + subTag, mTraceCookie + subCookie);
+ }
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 2da9fa6088db..13c42eb66b69 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -85,6 +85,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
private final @NonNull AudioService mAudioService;
private final @NonNull Context mContext;
+ private final @NonNull AudioSystemAdapter mAudioSystem;
/** ID for Communication strategy retrieved form audio policy manager */
private int mCommunicationStrategyId = -1;
@@ -159,12 +160,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
public static final long USE_SET_COMMUNICATION_DEVICE = 243827847L;
//-------------------------------------------------------------------
- /*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service) {
+ /*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service,
+ @NonNull AudioSystemAdapter audioSystem) {
mContext = context;
mAudioService = service;
mBtHelper = new BtHelper(this);
mDeviceInventory = new AudioDeviceInventory(this);
mSystemServer = SystemServerAdapter.getDefaultAdapter(mContext);
+ mAudioSystem = audioSystem;
init();
}
@@ -173,12 +176,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
* in system_server */
AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service,
@NonNull AudioDeviceInventory mockDeviceInventory,
- @NonNull SystemServerAdapter mockSystemServer) {
+ @NonNull SystemServerAdapter mockSystemServer,
+ @NonNull AudioSystemAdapter audioSystem) {
mContext = context;
mAudioService = service;
mBtHelper = new BtHelper(this);
mDeviceInventory = mockDeviceInventory;
mSystemServer = mockSystemServer;
+ mAudioSystem = audioSystem;
init();
}
@@ -540,7 +545,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
AudioAttributes attr =
AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
AudioSystem.STREAM_VOICE_CALL);
- List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(
+ List<AudioDeviceAttributes> devices = mAudioSystem.getDevicesForAttributes(
attr, false /* forVolume */);
if (devices.isEmpty()) {
if (mAudioService.isPlatformVoice()) {
@@ -1493,7 +1498,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
Log.v(TAG, "onSetForceUse(useCase<" + useCase + ">, config<" + config + ">, fromA2dp<"
+ fromA2dp + ">, eventSource<" + eventSource + ">)");
}
- AudioSystem.setForceUse(useCase, config);
+ mAudioSystem.setForceUse(useCase, config);
}
private void onSendBecomingNoisyIntent() {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index dbfb832e592a..3487fc2c14be 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -199,6 +199,7 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.audio.AudioServiceEvents.DeviceVolumeEvent;
import com.android.server.audio.AudioServiceEvents.PhoneStateEvent;
+import com.android.server.audio.AudioServiceEvents.VolChangedBroadcastEvent;
import com.android.server.audio.AudioServiceEvents.VolumeEvent;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
@@ -1214,7 +1215,7 @@ public class AudioService extends IAudioService.Stub
mUseFixedVolume = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume);
- mDeviceBroker = new AudioDeviceBroker(mContext, this);
+ mDeviceBroker = new AudioDeviceBroker(mContext, this, mAudioSystem);
mRecordMonitor = new RecordingActivityMonitor(mContext);
mRecordMonitor.registerRecordingCallback(mVoiceRecordingActivityMonitor, true);
@@ -1687,7 +1688,7 @@ public class AudioService extends IAudioService.Stub
synchronized (mSettingsLock) {
final int forDock = mDockAudioMediaEnabled ?
- AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE;
+ AudioSystem.FORCE_DIGITAL_DOCK : AudioSystem.FORCE_NONE;
mDeviceBroker.setForceUse_Async(AudioSystem.FOR_DOCK, forDock, "onAudioServerDied");
sendEncodedSurroundMode(mContentResolver, "onAudioServerDied");
sendEnabledSurroundFormats(mContentResolver, true);
@@ -2323,9 +2324,10 @@ public class AudioService extends IAudioService.Stub
SENDMSG_QUEUE,
AudioSystem.FOR_DOCK,
mDockAudioMediaEnabled ?
- AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE,
+ AudioSystem.FORCE_DIGITAL_DOCK : AudioSystem.FORCE_NONE,
new String("readDockAudioSettings"),
0);
+
}
@@ -3972,13 +3974,32 @@ public class AudioService extends IAudioService.Stub
Log.e(TAG, "Unsupported non-stream type based VolumeInfo", new Exception());
return;
}
+
int index = vi.getVolumeIndex();
if (index == VolumeInfo.INDEX_NOT_SET && !vi.hasMuteCommand()) {
throw new IllegalArgumentException(
"changing device volume requires a volume index or mute command");
}
- // TODO handle unmuting if current audio device
+ // force a cache clear to force reevaluating stream type to audio device selection
+ // that can interfere with the sending of the VOLUME_CHANGED_ACTION intent
+ mAudioSystem.clearRoutingCache();
+
+ // log the current device that will be used when evaluating the sending of the
+ // VOLUME_CHANGED_ACTION intent to see if the current device is the one being modified
+ final int currDev = getDeviceForStream(vi.getStreamType());
+
+ final boolean skipping = (currDev == ada.getInternalType());
+
+ AudioService.sVolumeLogger.enqueue(new DeviceVolumeEvent(vi.getStreamType(), index, ada,
+ currDev, callingPackage, skipping));
+
+ if (skipping) {
+ // setDeviceVolume was called on a device currently being used
+ return;
+ }
+
+ // TODO handle unmuting of current audio device
// if a stream is not muted but the VolumeInfo is for muting, set the volume index
// for the device to min volume
if (vi.hasMuteCommand() && vi.isMuted() && !isStreamMute(vi.getStreamType())) {
@@ -4129,11 +4150,11 @@ public class AudioService extends IAudioService.Stub
return;
}
- final EventLogger.Event event = (device == null)
- ? new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
- index/*val1*/, flags/*val2*/, callingPackage)
- : new DeviceVolumeEvent(streamType, index, device, callingPackage);
- sVolumeLogger.enqueue(event);
+ if (device == null) {
+ // call was already logged in setDeviceVolume()
+ sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
+ index/*val1*/, flags/*val2*/, callingPackage));
+ }
setStreamVolume(streamType, index, flags, device,
callingPackage, callingPackage, attributionTag,
Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
@@ -4547,7 +4568,11 @@ public class AudioService extends IAudioService.Stub
maybeSendSystemAudioStatusCommand(false);
}
}
- sendVolumeUpdate(streamType, oldIndex, index, flags, device);
+ if (ada == null) {
+ // only non-null when coming here from setDeviceVolume
+ // TODO change test to check early if device is current device or not
+ sendVolumeUpdate(streamType, oldIndex, index, flags, device);
+ }
}
private void dispatchAbsoluteVolumeChanged(int streamType, AbsoluteVolumeDeviceInfo deviceInfo,
@@ -8561,6 +8586,8 @@ public class AudioService extends IAudioService.Stub
mVolumeChanged.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE_ALIAS,
mStreamVolumeAlias[mStreamType]);
+ AudioService.sVolumeLogger.enqueue(new VolChangedBroadcastEvent(
+ mStreamType, mStreamVolumeAlias[mStreamType], index));
sendBroadcastToAll(mVolumeChanged, mVolumeChangedOptions);
}
}
@@ -10837,7 +10864,7 @@ public class AudioService extends IAudioService.Stub
static final int LOG_NB_EVENTS_PHONE_STATE = 20;
static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 50;
static final int LOG_NB_EVENTS_FORCE_USE = 20;
- static final int LOG_NB_EVENTS_VOLUME = 40;
+ static final int LOG_NB_EVENTS_VOLUME = 100;
static final int LOG_NB_EVENTS_DYN_POLICY = 10;
static final int LOG_NB_EVENTS_SPATIAL = 30;
static final int LOG_NB_EVENTS_SOUND_DOSE = 30;
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 58caf5ae8ca0..b022b5bb866a 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -147,20 +147,45 @@ public class AudioServiceEvents {
}
}
+ static final class VolChangedBroadcastEvent extends EventLogger.Event {
+ final int mStreamType;
+ final int mAliasStreamType;
+ final int mIndex;
+
+ VolChangedBroadcastEvent(int stream, int alias, int index) {
+ mStreamType = stream;
+ mAliasStreamType = alias;
+ mIndex = index;
+ }
+
+ @Override
+ public String eventToString() {
+ return new StringBuilder("sending VOLUME_CHANGED stream:")
+ .append(AudioSystem.streamToString(mStreamType))
+ .append(" index:").append(mIndex)
+ .append(" alias:").append(AudioSystem.streamToString(mAliasStreamType))
+ .toString();
+ }
+ }
+
static final class DeviceVolumeEvent extends EventLogger.Event {
final int mStream;
final int mVolIndex;
final String mDeviceNativeType;
final String mDeviceAddress;
final String mCaller;
+ final int mDeviceForStream;
+ final boolean mSkipped;
DeviceVolumeEvent(int streamType, int index, @NonNull AudioDeviceAttributes device,
- String callingPackage) {
+ int deviceForStream, String callingPackage, boolean skipped) {
mStream = streamType;
mVolIndex = index;
mDeviceNativeType = "0x" + Integer.toHexString(device.getInternalType());
mDeviceAddress = device.getAddress();
+ mDeviceForStream = deviceForStream;
mCaller = callingPackage;
+ mSkipped = skipped;
// log metrics
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_VOLUME_EVENT)
.set(MediaMetrics.Property.EVENT, "setDeviceVolume")
@@ -175,12 +200,18 @@ public class AudioServiceEvents {
@Override
public String eventToString() {
- return new StringBuilder("setDeviceVolume(stream:")
+ final StringBuilder sb = new StringBuilder("setDeviceVolume(stream:")
.append(AudioSystem.streamToString(mStream))
.append(" index:").append(mVolIndex)
.append(" device:").append(mDeviceNativeType)
.append(" addr:").append(mDeviceAddress)
- .append(") from ").append(mCaller).toString();
+ .append(") from ").append(mCaller);
+ if (mSkipped) {
+ sb.append(" skipped [device in use]");
+ } else {
+ sb.append(" currDevForStream:Ox").append(Integer.toHexString(mDeviceForStream));
+ }
+ return sb.toString();
}
}
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 7af7ed5fff65..d066340c42b2 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -162,6 +162,16 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
}
/**
+ * Empties the routing cache if enabled.
+ */
+ public void clearRoutingCache() {
+ if (DEBUG_CACHE) {
+ Log.d(TAG, "---- routing cache clear (from java) ----------");
+ }
+ invalidateRoutingCache();
+ }
+
+ /**
* @see AudioManager#addOnDevicesForAttributesChangedListener(
* AudioAttributes, Executor, OnDevicesForAttributesChangedListener)
*/
@@ -464,6 +474,7 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
* @return
*/
public int setParameters(String keyValuePairs) {
+ invalidateRoutingCache();
return AudioSystem.setParameters(keyValuePairs);
}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 5edd43464c6b..462c9381b904 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -77,7 +77,7 @@ public class SpatializerHelper {
//------------------------------------------------------------
- private static final SparseIntArray SPAT_MODE_FOR_DEVICE_TYPE = new SparseIntArray(15) {
+ private static final SparseIntArray SPAT_MODE_FOR_DEVICE_TYPE = new SparseIntArray(14) {
{
append(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, SpatializationMode.SPATIALIZER_TRANSAURAL);
append(AudioDeviceInfo.TYPE_WIRED_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
@@ -91,7 +91,6 @@ public class SpatializerHelper {
append(AudioDeviceInfo.TYPE_LINE_ANALOG, SpatializationMode.SPATIALIZER_TRANSAURAL);
append(AudioDeviceInfo.TYPE_LINE_DIGITAL, SpatializationMode.SPATIALIZER_TRANSAURAL);
append(AudioDeviceInfo.TYPE_AUX_LINE, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_HEARING_AID, SpatializationMode.SPATIALIZER_BINAURAL);
append(AudioDeviceInfo.TYPE_BLE_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
append(AudioDeviceInfo.TYPE_BLE_SPEAKER, SpatializationMode.SPATIALIZER_TRANSAURAL);
// assumption that BLE broadcast would be mostly consumed on headsets
diff --git a/services/core/java/com/android/server/biometrics/OWNERS b/services/core/java/com/android/server/biometrics/OWNERS
index cd281e06f40d..1bf2aeffdf0c 100644
--- a/services/core/java/com/android/server/biometrics/OWNERS
+++ b/services/core/java/com/android/server/biometrics/OWNERS
@@ -6,6 +6,10 @@ jaggies@google.com
jbolinger@google.com
jeffpu@google.com
joshmccloskey@google.com
+diyab@google.com
+austindelgado@google.com
+spdonghao@google.com
+wenhuiy@google.com
firewall@google.com
jasonsfchang@google.com
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java
index 2653ce76459d..d9947ddedcef 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java
@@ -95,8 +95,10 @@ public class AuthSessionCoordinator implements AuthSessionListener {
}
}
- mRingBuffer.addApiCall("internal : onAuthSessionEnded(" + mUserId + ")");
- clearSession();
+ if (mAuthOperations.isEmpty()) {
+ mRingBuffer.addApiCall("internal : onAuthSessionEnded(" + mUserId + ")");
+ clearSession();
+ }
}
private void clearSession() {
@@ -203,7 +205,7 @@ public class AuthSessionCoordinator implements AuthSessionListener {
return;
}
mAuthOperations.remove(sensorId);
- if (mIsAuthenticating && mAuthOperations.isEmpty()) {
+ if (mIsAuthenticating) {
endAuthSession();
}
}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 299f86550efd..378363c870c7 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -1160,6 +1160,14 @@ public class AutomaticBrightnessController {
update();
}
+ /**
+ * Convert a brightness float scale value to a nit value. Adjustments, such as RBC, are not
+ * applied. This is used when storing the brightness in nits for the default display and when
+ * passing the brightness value to follower displays.
+ *
+ * @param brightness The float scale value
+ * @return The nit value or -1f if no conversion is possible.
+ */
public float convertToNits(float brightness) {
if (mCurrentBrightnessMapper != null) {
return mCurrentBrightnessMapper.convertToNits(brightness);
@@ -1168,6 +1176,30 @@ public class AutomaticBrightnessController {
}
}
+ /**
+ * Convert a brightness float scale value to a nit value. Adjustments, such as RBC are applied.
+ * This is used when sending the brightness value to
+ * {@link com.android.server.display.BrightnessTracker}.
+ *
+ * @param brightness The float scale value
+ * @return The nit value or -1f if no conversion is possible.
+ */
+ public float convertToAdjustedNits(float brightness) {
+ if (mCurrentBrightnessMapper != null) {
+ return mCurrentBrightnessMapper.convertToAdjustedNits(brightness);
+ } else {
+ return -1.0f;
+ }
+ }
+
+ /**
+ * Convert a brightness nit value to a float scale value. It is assumed that the nit value
+ * provided does not have adjustments, such as RBC, applied.
+ *
+ * @param nits The nit value
+ * @return The float scale value or {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} if no
+ * conversion is possible.
+ */
public float convertToFloatScale(float nits) {
if (mCurrentBrightnessMapper != null) {
return mCurrentBrightnessMapper.convertToFloatScale(nits);
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 3456e3e262de..df2a8301f543 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -322,6 +322,14 @@ public abstract class BrightnessMappingStrategy {
public abstract float convertToNits(float brightness);
/**
+ * Converts the provided brightness value to nits if possible. Adjustments, such as RBC are
+ * applied.
+ *
+ * Returns -1.0f if there's no available mapping for the brightness to nits.
+ */
+ public abstract float convertToAdjustedNits(float brightness);
+
+ /**
* Converts the provided nit value to a float scale value if possible.
*
* Returns {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} if there's no available mapping for
@@ -683,6 +691,11 @@ public abstract class BrightnessMappingStrategy {
}
@Override
+ public float convertToAdjustedNits(float brightness) {
+ return -1.0f;
+ }
+
+ @Override
public float convertToFloatScale(float nits) {
return PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
@@ -804,6 +817,14 @@ public abstract class BrightnessMappingStrategy {
// a brightness in nits.
private Spline mBrightnessToNitsSpline;
+ // A spline mapping from nits with adjustments applied to the corresponding brightness
+ // value, normalized to the range [0, 1.0].
+ private Spline mAdjustedNitsToBrightnessSpline;
+
+ // A spline mapping from the system brightness value, normalized to the range [0, 1.0], to
+ // a brightness in nits with adjustments applied.
+ private Spline mBrightnessToAdjustedNitsSpline;
+
// The default brightness configuration.
private final BrightnessConfiguration mDefaultConfig;
@@ -843,6 +864,8 @@ public abstract class BrightnessMappingStrategy {
mNits = nits;
mBrightness = brightness;
computeNitsBrightnessSplines(mNits);
+ mAdjustedNitsToBrightnessSpline = mNitsToBrightnessSpline;
+ mBrightnessToAdjustedNitsSpline = mBrightnessToNitsSpline;
mDefaultConfig = config;
if (mLoggingEnabled) {
@@ -892,7 +915,7 @@ public abstract class BrightnessMappingStrategy {
nits = mDisplayWhiteBalanceController.calculateAdjustedBrightnessNits(nits);
}
- float brightness = mNitsToBrightnessSpline.interpolate(nits);
+ float brightness = mAdjustedNitsToBrightnessSpline.interpolate(nits);
// Correct the brightness according to the current application and its category, but
// only if no user data point is set (as this will override the user setting).
if (mUserLux == -1) {
@@ -930,6 +953,11 @@ public abstract class BrightnessMappingStrategy {
}
@Override
+ public float convertToAdjustedNits(float brightness) {
+ return mBrightnessToAdjustedNitsSpline.interpolate(brightness);
+ }
+
+ @Override
public float convertToFloatScale(float nits) {
return mNitsToBrightnessSpline.interpolate(nits);
}
@@ -989,7 +1017,13 @@ public abstract class BrightnessMappingStrategy {
@Override
public void recalculateSplines(boolean applyAdjustment, float[] adjustedNits) {
mBrightnessRangeAdjustmentApplied = applyAdjustment;
- computeNitsBrightnessSplines(mBrightnessRangeAdjustmentApplied ? adjustedNits : mNits);
+ if (applyAdjustment) {
+ mAdjustedNitsToBrightnessSpline = Spline.createSpline(adjustedNits, mBrightness);
+ mBrightnessToAdjustedNitsSpline = Spline.createSpline(mBrightness, adjustedNits);
+ } else {
+ mAdjustedNitsToBrightnessSpline = mNitsToBrightnessSpline;
+ mBrightnessToAdjustedNitsSpline = mBrightnessToNitsSpline;
+ }
}
@Override
@@ -999,6 +1033,8 @@ public abstract class BrightnessMappingStrategy {
pw.println(" mBrightnessSpline=" + mBrightnessSpline);
pw.println(" mNitsToBrightnessSpline=" + mNitsToBrightnessSpline);
pw.println(" mBrightnessToNitsSpline=" + mBrightnessToNitsSpline);
+ pw.println(" mAdjustedNitsToBrightnessSpline=" + mAdjustedNitsToBrightnessSpline);
+ pw.println(" mAdjustedBrightnessToNitsSpline=" + mBrightnessToAdjustedNitsSpline);
pw.println(" mMaxGamma=" + mMaxGamma);
pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
pw.println(" mUserLux=" + mUserLux);
@@ -1072,7 +1108,7 @@ public abstract class BrightnessMappingStrategy {
float defaultNits = defaultSpline.interpolate(lux);
float longTermNits = currSpline.interpolate(lux);
float shortTermNits = mBrightnessSpline.interpolate(lux);
- float brightness = mNitsToBrightnessSpline.interpolate(shortTermNits);
+ float brightness = mAdjustedNitsToBrightnessSpline.interpolate(shortTermNits);
String luxPrefix = (lux == mUserLux ? "^" : "");
String strLux = luxPrefix + toStrFloatForDump(lux);
@@ -1146,7 +1182,7 @@ public abstract class BrightnessMappingStrategy {
float[] defaultNits = defaultCurve.second;
float[] defaultBrightness = new float[defaultNits.length];
for (int i = 0; i < defaultBrightness.length; i++) {
- defaultBrightness[i] = mNitsToBrightnessSpline.interpolate(defaultNits[i]);
+ defaultBrightness[i] = mAdjustedNitsToBrightnessSpline.interpolate(defaultNits[i]);
}
Pair<float[], float[]> curve = getAdjustedCurve(defaultLux, defaultBrightness, mUserLux,
mUserBrightness, mAutoBrightnessAdjustment, mMaxGamma);
@@ -1154,7 +1190,7 @@ public abstract class BrightnessMappingStrategy {
float[] brightness = curve.second;
float[] nits = new float[brightness.length];
for (int i = 0; i < nits.length; i++) {
- nits[i] = mBrightnessToNitsSpline.interpolate(brightness[i]);
+ nits[i] = mBrightnessToAdjustedNitsSpline.interpolate(brightness[i]);
}
mBrightnessSpline = Spline.createSpline(lux, nits);
}
@@ -1162,7 +1198,7 @@ public abstract class BrightnessMappingStrategy {
private float getUnadjustedBrightness(float lux) {
Pair<float[], float[]> curve = mConfig.getCurve();
Spline spline = Spline.createSpline(curve.first, curve.second);
- return mNitsToBrightnessSpline.interpolate(spline.interpolate(lux));
+ return mAdjustedNitsToBrightnessSpline.interpolate(spline.interpolate(lux));
}
private float correctBrightness(float brightness, String packageName, int category) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index a021174e3031..ca482dc41ce5 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -2396,7 +2396,6 @@ public class DisplayDeviceConfig {
mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000;
mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000;
mHbmData.timeMinMillis = hbmTiming.getTimeMinSecs_all().longValue() * 1000;
- mHbmData.thermalStatusLimit = convertThermalStatus(hbm.getThermalStatusLimit_all());
mHbmData.allowInLowPowerMode = hbm.getAllowInLowPowerMode_all();
final RefreshRateRange rr = hbm.getRefreshRate_all();
if (rr != null) {
@@ -2972,9 +2971,6 @@ public class DisplayDeviceConfig {
/** Brightness level at which we transition from normal to high-brightness. */
public float transitionPoint;
- /** Enable HBM only if the thermal status is not higher than this. */
- public @PowerManager.ThermalStatus int thermalStatusLimit;
-
/** Whether HBM is allowed when {@code Settings.Global.LOW_POWER_MODE} is active. */
public boolean allowInLowPowerMode;
@@ -2993,15 +2989,13 @@ public class DisplayDeviceConfig {
HighBrightnessModeData() {}
HighBrightnessModeData(float minimumLux, float transitionPoint, long timeWindowMillis,
- long timeMaxMillis, long timeMinMillis,
- @PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode,
+ long timeMaxMillis, long timeMinMillis, boolean allowInLowPowerMode,
float minimumHdrPercentOfScreen) {
this.minimumLux = minimumLux;
this.transitionPoint = transitionPoint;
this.timeWindowMillis = timeWindowMillis;
this.timeMaxMillis = timeMaxMillis;
this.timeMinMillis = timeMinMillis;
- this.thermalStatusLimit = thermalStatusLimit;
this.allowInLowPowerMode = allowInLowPowerMode;
this.minimumHdrPercentOfScreen = minimumHdrPercentOfScreen;
}
@@ -3016,7 +3010,6 @@ public class DisplayDeviceConfig {
other.timeMaxMillis = timeMaxMillis;
other.timeMinMillis = timeMinMillis;
other.transitionPoint = transitionPoint;
- other.thermalStatusLimit = thermalStatusLimit;
other.allowInLowPowerMode = allowInLowPowerMode;
other.minimumHdrPercentOfScreen = minimumHdrPercentOfScreen;
}
@@ -3029,7 +3022,6 @@ public class DisplayDeviceConfig {
+ ", timeWindow: " + timeWindowMillis + "ms"
+ ", timeMax: " + timeMaxMillis + "ms"
+ ", timeMin: " + timeMinMillis + "ms"
- + ", thermalStatusLimit: " + thermalStatusLimit
+ ", allowInLowPowerMode: " + allowInLowPowerMode
+ ", minimumHdrPercentOfScreen: " + minimumHdrPercentOfScreen
+ "} ";
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 5d92c7f8cf05..26b6cb0ebe9b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1781,7 +1781,24 @@ public final class DisplayManagerService extends SystemService {
} else {
configurePreferredDisplayModeLocked(display);
}
- addDisplayPowerControllerLocked(display);
+ DisplayPowerControllerInterface dpc = addDisplayPowerControllerLocked(display);
+
+ if (dpc != null) {
+ final int leadDisplayId = display.getLeadDisplayIdLocked();
+ updateDisplayPowerControllerLeaderLocked(dpc, leadDisplayId);
+
+ // Loop through all the displays and check if any should follow this one - it could be
+ // that the follower display was added before the lead display.
+ mLogicalDisplayMapper.forEachLocked(d -> {
+ if (d.getLeadDisplayIdLocked() == displayId) {
+ DisplayPowerControllerInterface followerDpc =
+ mDisplayPowerControllers.get(d.getDisplayIdLocked());
+ if (followerDpc != null) {
+ updateDisplayPowerControllerLeaderLocked(followerDpc, displayId);
+ }
+ }
+ });
+ }
mDisplayStates.append(displayId, Display.STATE_UNKNOWN);
@@ -1832,8 +1849,8 @@ public final class DisplayManagerService extends SystemService {
}
}
- private void updateDisplayPowerControllerLeaderLocked(DisplayPowerControllerInterface dpc,
- int leadDisplayId) {
+ private void updateDisplayPowerControllerLeaderLocked(
+ @NonNull DisplayPowerControllerInterface dpc, int leadDisplayId) {
if (dpc.getLeadDisplayId() == leadDisplayId) {
// Lead display hasn't changed, nothing to do.
return;
@@ -1851,9 +1868,11 @@ public final class DisplayManagerService extends SystemService {
// And then, if it's following, register it with the new one.
if (leadDisplayId != Layout.NO_LEAD_DISPLAY) {
- final DisplayPowerControllerInterface newLead =
+ final DisplayPowerControllerInterface newLeader =
mDisplayPowerControllers.get(leadDisplayId);
- newLead.addDisplayBrightnessFollower(dpc);
+ if (newLeader != null) {
+ newLeader.addDisplayBrightnessFollower(dpc);
+ }
}
}
@@ -1872,6 +1891,7 @@ public final class DisplayManagerService extends SystemService {
final DisplayPowerControllerInterface dpc =
mDisplayPowerControllers.removeReturnOld(displayId);
if (dpc != null) {
+ updateDisplayPowerControllerLeaderLocked(dpc, Layout.NO_LEAD_DISPLAY);
dpc.stop();
}
mDisplayStates.delete(displayId);
@@ -3062,10 +3082,11 @@ public final class DisplayManagerService extends SystemService {
}
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
- private void addDisplayPowerControllerLocked(LogicalDisplay display) {
+ private DisplayPowerControllerInterface addDisplayPowerControllerLocked(
+ LogicalDisplay display) {
if (mPowerHandler == null) {
// initPowerManagement has not yet been called.
- return;
+ return null;
}
if (mBrightnessTracker == null && display.getDisplayIdLocked() == Display.DEFAULT_DISPLAY) {
@@ -3086,7 +3107,7 @@ public final class DisplayManagerService extends SystemService {
if (hbmMetadata == null) {
Slog.wtf(TAG, "High Brightness Mode Metadata is null in DisplayManagerService for "
+ "display: " + display.getDisplayIdLocked());
- return;
+ return null;
}
if (DeviceConfig.getBoolean("display_manager",
"use_newly_structured_display_power_controller", true)) {
@@ -3101,6 +3122,7 @@ public final class DisplayManagerService extends SystemService {
() -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
}
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
+ return displayPowerController;
}
private void handleBrightnessChange(LogicalDisplay display) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 1acc20880258..78c5f0eee492 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -789,6 +789,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
}
+ @GuardedBy("mLock")
+ private void clearDisplayBrightnessFollowersLocked() {
+ for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) {
+ DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i);
+ mHandler.postAtTime(() -> follower.setBrightnessToFollow(
+ PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1,
+ /* ambientLux= */ 0), mClock.uptimeMillis());
+ }
+ mDisplayBrightnessFollowers.clear();
+ }
+
@Nullable
@Override
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@@ -946,6 +957,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
@Override
public void stop() {
synchronized (mLock) {
+ clearDisplayBrightnessFollowersLocked();
+
mStopped = true;
Message msg = mHandler.obtainMessage(MSG_STOP);
mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
@@ -1039,7 +1052,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
noteScreenBrightness(mPowerState.getScreenBrightness());
// Initialize all of the brightness tracking state
- final float brightness = convertToNits(mPowerState.getScreenBrightness());
+ final float brightness = convertToAdjustedNits(mPowerState.getScreenBrightness());
if (mBrightnessTracker != null && brightness >= PowerManager.BRIGHTNESS_MIN) {
mBrightnessTracker.start(brightness);
}
@@ -2698,7 +2711,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
boolean wasShortTermModelActive) {
- final float brightnessInNits = convertToNits(brightness);
+ final float brightnessInNits = convertToAdjustedNits(brightness);
if (mUseAutoBrightness && brightnessInNits >= 0.0f
&& mAutomaticBrightnessController != null && mBrightnessTracker != null) {
// We only want to track changes on devices that can actually map the display backlight
@@ -2722,6 +2735,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
return mAutomaticBrightnessController.convertToNits(brightness);
}
+ private float convertToAdjustedNits(float brightness) {
+ if (mAutomaticBrightnessController == null) {
+ return -1f;
+ }
+ return mAutomaticBrightnessController.convertToAdjustedNits(brightness);
+ }
+
private float convertToFloatScale(float nits) {
if (mAutomaticBrightnessController == null) {
return PowerManager.BRIGHTNESS_INVALID_FLOAT;
@@ -2808,6 +2828,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
pw.println(" mDisplayId=" + mDisplayId);
pw.println(" mLeadDisplayId=" + mLeadDisplayId);
pw.println(" mLightSensor=" + mLightSensor);
+ pw.println(" mDisplayBrightnessFollowers=" + mDisplayBrightnessFollowers);
pw.println();
pw.println("Display Power Controller Locked State:");
@@ -3075,16 +3096,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
int appliedRbcStrength = event.isRbcEnabled() ? event.getRbcStrength() : -1;
float appliedHbmMaxNits =
event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF
- ? -1f : convertToNits(event.getHbmMax());
+ ? -1f : convertToAdjustedNits(event.getHbmMax());
// thermalCapNits set to -1 if not currently capping max brightness
float appliedThermalCapNits =
event.getThermalMax() == PowerManager.BRIGHTNESS_MAX
- ? -1f : convertToNits(event.getThermalMax());
+ ? -1f : convertToAdjustedNits(event.getThermalMax());
if (mIsDisplayInternal) {
FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
- convertToNits(event.getInitialBrightness()),
- convertToNits(event.getBrightness()),
+ convertToAdjustedNits(event.getInitialBrightness()),
+ convertToAdjustedNits(event.getBrightness()),
event.getLux(),
event.getPhysicalDisplayId(),
event.wasShortTermModelActive(),
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index b36aedefa2a7..a76f907a41f5 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -764,6 +764,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
@Override
public void stop() {
synchronized (mLock) {
+ clearDisplayBrightnessFollowersLocked();
+
mStopped = true;
Message msg = mHandler.obtainMessage(MSG_STOP);
mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
@@ -854,7 +856,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
noteScreenBrightness(mPowerState.getScreenBrightness());
// Initialize all of the brightness tracking state
- final float brightness = mDisplayBrightnessController.convertToNits(
+ final float brightness = mDisplayBrightnessController.convertToAdjustedNits(
mPowerState.getScreenBrightness());
if (mBrightnessTracker != null && brightness >= PowerManager.BRIGHTNESS_MIN) {
mBrightnessTracker.start(brightness);
@@ -2165,7 +2167,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
boolean wasShortTermModelActive) {
- final float brightnessInNits = mDisplayBrightnessController.convertToNits(brightness);
+ final float brightnessInNits =
+ mDisplayBrightnessController.convertToAdjustedNits(brightness);
if (mAutomaticBrightnessStrategy.shouldUseAutoBrightness() && brightnessInNits >= 0.0f
&& mAutomaticBrightnessController != null && mBrightnessTracker != null) {
// We only want to track changes on devices that can actually map the display backlight
@@ -2200,6 +2203,17 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
}
}
+ @GuardedBy("mLock")
+ private void clearDisplayBrightnessFollowersLocked() {
+ for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) {
+ DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i);
+ mHandler.postAtTime(() -> follower.setBrightnessToFollow(
+ PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1,
+ /* ambientLux= */ 0), mClock.uptimeMillis());
+ }
+ mDisplayBrightnessFollowers.clear();
+ }
+
@Override
public void dump(final PrintWriter pw) {
synchronized (mLock) {
@@ -2208,6 +2222,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
pw.println(" mDisplayId=" + mDisplayId);
pw.println(" mLeadDisplayId=" + mLeadDisplayId);
pw.println(" mLightSensor=" + mLightSensor);
+ pw.println(" mDisplayBrightnessFollowers=" + mDisplayBrightnessFollowers);
pw.println();
pw.println("Display Power Controller Locked State:");
@@ -2438,15 +2453,16 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
int appliedRbcStrength = event.isRbcEnabled() ? event.getRbcStrength() : -1;
float appliedHbmMaxNits =
event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF
- ? -1f : mDisplayBrightnessController.convertToNits(event.getHbmMax());
+ ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getHbmMax());
// thermalCapNits set to -1 if not currently capping max brightness
float appliedThermalCapNits =
event.getThermalMax() == PowerManager.BRIGHTNESS_MAX
- ? -1f : mDisplayBrightnessController.convertToNits(event.getThermalMax());
+ ? -1f : mDisplayBrightnessController.convertToAdjustedNits(event.getThermalMax());
if (mIsDisplayInternal) {
FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
- mDisplayBrightnessController.convertToNits(event.getInitialBrightness()),
- mDisplayBrightnessController.convertToNits(event.getBrightness()),
+ mDisplayBrightnessController
+ .convertToAdjustedNits(event.getInitialBrightness()),
+ mDisplayBrightnessController.convertToAdjustedNits(event.getBrightness()),
event.getLux(),
event.getPhysicalDisplayId(),
event.wasShortTermModelActive(),
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index ca208ac73290..11160a532609 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -22,13 +22,8 @@ import android.hardware.display.BrightnessInfo;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
-import android.os.IThermalEventListener;
-import android.os.IThermalService;
import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
-import android.os.Temperature;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.MathUtils;
@@ -75,7 +70,6 @@ class HighBrightnessModeController {
private final Runnable mHbmChangeCallback;
private final Runnable mRecalcRunnable;
private final Clock mClock;
- private final SkinThermalStatusObserver mSkinThermalStatusObserver;
private final Context mContext;
private final SettingsObserver mSettingsObserver;
private final Injector mInjector;
@@ -100,10 +94,8 @@ class HighBrightnessModeController {
private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
private boolean mIsHdrLayerPresent = false;
-
// mMaxDesiredHdrSdrRatio should only be applied when there is a valid backlight->nits mapping
private float mMaxDesiredHdrSdrRatio = DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
- private boolean mIsThermalStatusWithinLimit = true;
private boolean mIsBlockedByLowPowerMode = false;
private int mWidth;
private int mHeight;
@@ -138,7 +130,6 @@ class HighBrightnessModeController {
mBrightnessMax = brightnessMax;
mHbmChangeCallback = hbmChangeCallback;
mHighBrightnessModeMetadata = hbmMetadata;
- mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mRecalcRunnable = this::recalculateTimeAllowance;
mHdrListener = new HdrListener();
@@ -261,7 +252,6 @@ class HighBrightnessModeController {
void stop() {
registerHdrListener(null /*displayToken*/);
- mSkinThermalStatusObserver.stopObserving();
mSettingsObserver.stopObserving();
}
@@ -278,15 +268,10 @@ class HighBrightnessModeController {
mDisplayStatsId = displayUniqueId.hashCode();
unregisterHdrListener();
- mSkinThermalStatusObserver.stopObserving();
mSettingsObserver.stopObserving();
if (deviceSupportsHbm()) {
registerHdrListener(displayToken);
recalculateTimeAllowance();
- if (mHbmData.thermalStatusLimit > PowerManager.THERMAL_STATUS_NONE) {
- mIsThermalStatusWithinLimit = true;
- mSkinThermalStatusObserver.startObserving();
- }
if (!mHbmData.allowInLowPowerMode) {
mIsBlockedByLowPowerMode = false;
mSettingsObserver.startObserving();
@@ -327,7 +312,6 @@ class HighBrightnessModeController {
pw.println(" mIsTimeAvailable= " + mIsTimeAvailable);
pw.println(" mRunningStartTimeMillis="
+ TimeUtils.formatUptime(mHighBrightnessModeMetadata.getRunningStartTimeMillis()));
- pw.println(" mIsThermalStatusWithinLimit=" + mIsThermalStatusWithinLimit);
pw.println(" mIsBlockedByLowPowerMode=" + mIsBlockedByLowPowerMode);
pw.println(" width*height=" + mWidth + "*" + mHeight);
pw.println(" mEvents=");
@@ -344,8 +328,6 @@ class HighBrightnessModeController {
}
lastStartTime = dumpHbmEvent(pw, event);
}
-
- mSkinThermalStatusObserver.dump(pw);
}
private long dumpHbmEvent(PrintWriter pw, HbmEvent event) {
@@ -367,7 +349,7 @@ class HighBrightnessModeController {
// See {@link #getHdrBrightnessValue}.
return !mIsHdrLayerPresent
&& (mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange
- && mIsThermalStatusWithinLimit && !mIsBlockedByLowPowerMode);
+ && !mIsBlockedByLowPowerMode);
}
private boolean deviceSupportsHbm() {
@@ -469,7 +451,6 @@ class HighBrightnessModeController {
+ ", isAutoBrightnessEnabled: " + mIsAutoBrightnessEnabled
+ ", mIsTimeAvailable: " + mIsTimeAvailable
+ ", mIsInAllowedAmbientRange: " + mIsInAllowedAmbientRange
- + ", mIsThermalStatusWithinLimit: " + mIsThermalStatusWithinLimit
+ ", mIsBlockedByLowPowerMode: " + mIsBlockedByLowPowerMode
+ ", mBrightness: " + mBrightness
+ ", mUnthrottledBrightness: " + mUnthrottledBrightness
@@ -499,13 +480,12 @@ class HighBrightnessModeController {
}
private void updateHbmStats(int newMode) {
- final float transitionPoint = mHbmData.transitionPoint;
int state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF;
if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
- && getHdrBrightnessValue() > transitionPoint) {
+ && getHdrBrightnessValue() > mHbmData.transitionPoint) {
state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR;
} else if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT
- && mBrightness > transitionPoint) {
+ && mBrightness > mHbmData.transitionPoint) {
state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT;
}
if (state == mHbmStatsState) {
@@ -519,16 +499,6 @@ class HighBrightnessModeController {
final boolean newHbmSv =
(state == FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT);
if (oldHbmSv && !newHbmSv) {
- // HighBrightnessModeController (HBMC) currently supports throttling from two sources:
- // 1. Internal, received from HBMC.SkinThermalStatusObserver.notifyThrottling()
- // 2. External, received from HBMC.onBrightnessChanged()
- // TODO(b/216373254): Deprecate internal throttling source
- final boolean internalThermalThrottling = !mIsThermalStatusWithinLimit;
- final boolean externalThermalThrottling =
- mUnthrottledBrightness > transitionPoint && // We would've liked HBM brightness...
- mBrightness <= transitionPoint && // ...but we got NBM, because of...
- mThrottlingReason == BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL; // ...thermals.
-
// If more than one conditions are flipped and turn off HBM sunlight
// visibility, only one condition will be reported to make it simple.
if (!mIsAutoBrightnessEnabled && mIsAutoBrightnessOffByState) {
@@ -541,7 +511,7 @@ class HighBrightnessModeController {
reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP;
} else if (!mIsTimeAvailable) {
reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT;
- } else if (internalThermalThrottling || externalThermalThrottling) {
+ } else if (isThermalThrottlingActive()) {
reason = FrameworkStatsLog
.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT;
} else if (mIsHdrLayerPresent) {
@@ -561,6 +531,14 @@ class HighBrightnessModeController {
mHbmStatsState = state;
}
+ @VisibleForTesting
+ boolean isThermalThrottlingActive() {
+ // We would've liked HBM, but we got NBM (normal brightness mode) because of thermals.
+ return mUnthrottledBrightness > mHbmData.transitionPoint
+ && mBrightness <= mHbmData.transitionPoint
+ && mThrottlingReason == BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
+ }
+
private String hbmStatsStateToString(int hbmStatsState) {
switch (hbmStatsState) {
case FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF:
@@ -635,82 +613,6 @@ class HighBrightnessModeController {
}
}
- private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
- private final Injector mInjector;
- private final Handler mHandler;
-
- private IThermalService mThermalService;
- private boolean mStarted;
-
- SkinThermalStatusObserver(Injector injector, Handler handler) {
- mInjector = injector;
- mHandler = handler;
- }
-
- @Override
- public void notifyThrottling(Temperature temp) {
- if (DEBUG) {
- Slog.d(TAG, "New thermal throttling status "
- + ", current thermal status = " + temp.getStatus()
- + ", threshold = " + mHbmData.thermalStatusLimit);
- }
- mHandler.post(() -> {
- mIsThermalStatusWithinLimit = temp.getStatus() <= mHbmData.thermalStatusLimit;
- // This recalculates HbmMode and runs mHbmChangeCallback if the mode has changed
- updateHbmMode();
- });
- }
-
- void startObserving() {
- if (mStarted) {
- if (DEBUG) {
- Slog.d(TAG, "Thermal status observer already started");
- }
- return;
- }
- mThermalService = mInjector.getThermalService();
- if (mThermalService == null) {
- Slog.w(TAG, "Could not observe thermal status. Service not available");
- return;
- }
- try {
- // We get a callback immediately upon registering so there's no need to query
- // for the current value.
- mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
- mStarted = true;
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to register thermal status listener", e);
- }
- }
-
- void stopObserving() {
- mIsThermalStatusWithinLimit = true;
- if (!mStarted) {
- if (DEBUG) {
- Slog.d(TAG, "Stop skipped because thermal status observer not started");
- }
- return;
- }
- try {
- mThermalService.unregisterThermalEventListener(this);
- mStarted = false;
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to unregister thermal status listener", e);
- }
- mThermalService = null;
- }
-
- void dump(PrintWriter writer) {
- writer.println(" SkinThermalStatusObserver:");
- writer.println(" mStarted: " + mStarted);
- if (mThermalService != null) {
- writer.println(" ThermalService available");
- } else {
- writer.println(" ThermalService not available");
- }
- }
- }
-
private final class SettingsObserver extends ContentObserver {
private final Uri mLowPowerModeSetting = Settings.Global.getUriFor(
Settings.Global.LOW_POWER_MODE);
@@ -766,11 +668,6 @@ class HighBrightnessModeController {
return SystemClock::uptimeMillis;
}
- public IThermalService getThermalService() {
- return IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
- }
-
public void reportHbmStateChange(int display, int state, int reason) {
FrameworkStatsLog.write(
FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED, display, state, reason);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index dab00d8070d4..0b6d1c851dc4 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -181,6 +181,19 @@ final class LogicalDisplay {
*/
private String mThermalBrightnessThrottlingDataId;
+ /**
+ * Refresh rate range limitation based on the current device layout
+ */
+ @Nullable
+ private SurfaceControl.RefreshRateRange mLayoutLimitedRefreshRate;
+
+ /**
+ * RefreshRateRange limitation for @Temperature.ThrottlingStatus
+ */
+ @NonNull
+ private SparseArray<SurfaceControl.RefreshRateRange> mThermalRefreshRateThrottling =
+ new SparseArray<>();
+
public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
mDisplayId = displayId;
mLayerStack = layerStack;
@@ -339,24 +352,24 @@ final class LogicalDisplay {
*/
public void updateLayoutLimitedRefreshRateLocked(
@Nullable SurfaceControl.RefreshRateRange layoutLimitedRefreshRate) {
- if (!Objects.equals(layoutLimitedRefreshRate, mBaseDisplayInfo.layoutLimitedRefreshRate)) {
- mBaseDisplayInfo.layoutLimitedRefreshRate = layoutLimitedRefreshRate;
- mInfo.set(null);
+ if (!Objects.equals(layoutLimitedRefreshRate, mLayoutLimitedRefreshRate)) {
+ mLayoutLimitedRefreshRate = layoutLimitedRefreshRate;
+ mDirty = true;
}
}
/**
- * Updates refreshRateThermalThrottling
+ * Updates thermalRefreshRateThrottling
*
- * @param refreshRanges new refreshRateThermalThrottling ranges limited by layout or default
+ * @param refreshRanges new thermalRefreshRateThrottling ranges limited by layout or default
*/
public void updateThermalRefreshRateThrottling(
@Nullable SparseArray<SurfaceControl.RefreshRateRange> refreshRanges) {
if (refreshRanges == null) {
refreshRanges = new SparseArray<>();
}
- if (!mBaseDisplayInfo.refreshRateThermalThrottling.contentEquals(refreshRanges)) {
- mBaseDisplayInfo.refreshRateThermalThrottling = refreshRanges;
- mInfo.set(null);
+ if (!mThermalRefreshRateThrottling.contentEquals(refreshRanges)) {
+ mThermalRefreshRateThrottling = refreshRanges;
+ mDirty = true;
}
}
@@ -499,6 +512,9 @@ final class LogicalDisplay {
mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT;
}
+ mBaseDisplayInfo.layoutLimitedRefreshRate = mLayoutLimitedRefreshRate;
+ mBaseDisplayInfo.thermalRefreshRateThrottling = mThermalRefreshRateThrottling;
+
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo.set(null);
mDirty = false;
@@ -952,6 +968,8 @@ final class LogicalDisplay {
pw.println("mDisplayGroupName=" + mDisplayGroupName);
pw.println("mThermalBrightnessThrottlingDataId=" + mThermalBrightnessThrottlingDataId);
pw.println("mLeadDisplayId=" + mLeadDisplayId);
+ pw.println("mLayoutLimitedRefreshRate=" + mLayoutLimitedRefreshRate);
+ pw.println("mThermalRefreshRateThrottling=" + mThermalRefreshRateThrottling);
}
@Override
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 2916fefefe1b..a3f8c4d16cd1 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -312,7 +312,10 @@ public final class DisplayBrightnessController {
}
/**
- * Convert a brightness float scale value to a nit value.
+ * Convert a brightness float scale value to a nit value. Adjustments, such as RBC, are not
+ * applied. This is used when storing the brightness in nits for the default display and when
+ * passing the brightness value to follower displays.
+ *
* @param brightness The float scale value
* @return The nit value or -1f if no conversion is possible.
*/
@@ -324,7 +327,24 @@ public final class DisplayBrightnessController {
}
/**
- * Convert a brightness nit value to a float scale value.
+ * Convert a brightness float scale value to a nit value. Adjustments, such as RBC are applied.
+ * This is used when sending the brightness value to
+ * {@link com.android.server.display.BrightnessTracker}.
+ *
+ * @param brightness The float scale value
+ * @return The nit value or -1f if no conversion is possible.
+ */
+ public float convertToAdjustedNits(float brightness) {
+ if (mAutomaticBrightnessController == null) {
+ return -1f;
+ }
+ return mAutomaticBrightnessController.convertToAdjustedNits(brightness);
+ }
+
+ /**
+ * Convert a brightness nit value to a float scale value. It is assumed that the nit value
+ * provided does not have adjustments, such as RBC, applied.
+ *
* @param nits The nit value
* @return The float scale value or {@link PowerManager.BRIGHTNESS_INVALID_FLOAT} if no
* conversion is possible.
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 01892945ed17..06b7698e9df2 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -65,6 +65,7 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.display.BrightnessSynchronizer;
+import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.display.DisplayDeviceConfig;
@@ -1171,7 +1172,7 @@ public class DisplayModeDirector {
public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 2;
// SETTING_MIN_RENDER_FRAME_RATE is used to propose a lower bound of the render frame rate.
- // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
+ // It votes [minRefreshRate, Float.POSITIVE_INFINITY]
public static final int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;
// APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render
@@ -1376,10 +1377,10 @@ public class DisplayModeDirector {
@VisibleForTesting
final class SettingsObserver extends ContentObserver {
- private final Uri mPeakRefreshRateSetting =
- Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
- private final Uri mMinRefreshRateSetting =
- Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE);
+ private final Uri mSmoothDisplaySetting =
+ Settings.System.getUriFor(Settings.System.SMOOTH_DISPLAY);
+ private final Uri mForcePeakRefreshRateSetting =
+ Settings.System.getUriFor(Settings.System.FORCE_PEAK_REFRESH_RATE);
private final Uri mLowPowerModeSetting =
Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
private final Uri mMatchContentFrameRateSetting =
@@ -1415,9 +1416,8 @@ public class DisplayModeDirector {
public void observe() {
final ContentResolver cr = mContext.getContentResolver();
- mInjector.registerPeakRefreshRateObserver(cr, this);
- cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
- UserHandle.USER_SYSTEM);
+ mInjector.registerSmoothDisplayObserver(cr, this);
+ mInjector.registerForcePeakRefreshRateObserver(cr, this);
cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
UserHandle.USER_SYSTEM);
cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
@@ -1459,8 +1459,8 @@ public class DisplayModeDirector {
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
synchronized (mLock) {
- if (mPeakRefreshRateSetting.equals(uri)
- || mMinRefreshRateSetting.equals(uri)) {
+ if (mSmoothDisplaySetting.equals(uri)
+ || mForcePeakRefreshRateSetting.equals(uri)) {
updateRefreshRateSettingLocked();
} else if (mLowPowerModeSetting.equals(uri)) {
updateLowPowerModeSettingLocked();
@@ -1515,12 +1515,9 @@ public class DisplayModeDirector {
}
private void updateRefreshRateSettingLocked() {
- final ContentResolver cr = mContext.getContentResolver();
- float minRefreshRate = Settings.System.getFloatForUser(cr,
- Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
- float peakRefreshRate = Settings.System.getFloatForUser(cr,
- Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId());
- updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
+ updateRefreshRateSettingLocked(RefreshRateSettingsUtils.getMinRefreshRate(mContext),
+ RefreshRateSettingsUtils.getPeakRefreshRate(mContext, mDefaultPeakRefreshRate),
+ mDefaultRefreshRate);
}
private void updateRefreshRateSettingLocked(
@@ -1708,14 +1705,13 @@ public class DisplayModeDirector {
}
public void observe() {
- DisplayManager dm = mContext.getSystemService(DisplayManager.class);
- dm.registerDisplayListener(this, mHandler);
+ mInjector.registerDisplayListener(this, mHandler);
// Populate existing displays
SparseArray<Display.Mode[]> modes = new SparseArray<>();
SparseArray<Display.Mode> defaultModes = new SparseArray<>();
DisplayInfo info = new DisplayInfo();
- Display[] displays = dm.getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
+ Display[] displays = mInjector.getDisplays();
for (Display d : displays) {
final int displayId = d.getDisplayId();
d.getDisplayInfo(info);
@@ -1754,17 +1750,9 @@ public class DisplayModeDirector {
updateLayoutLimitedFrameRate(displayId, displayInfo);
}
- @Nullable
private DisplayInfo getDisplayInfo(int displayId) {
- Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
- if (d == null) {
- // We can occasionally get a display added or changed event for a display that was
- // subsequently removed, which means this returns null. Check this case and bail
- // out early; if it gets re-attached we'll eventually get another call back for it.
- return null;
- }
DisplayInfo info = new DisplayInfo();
- d.getDisplayInfo(info);
+ mInjector.getDisplayInfo(displayId, info);
return info;
}
@@ -2435,8 +2423,7 @@ public class DisplayModeDirector {
}
private void updateDefaultDisplayState() {
- Display display = mContext.getSystemService(DisplayManager.class)
- .getDisplay(Display.DEFAULT_DISPLAY);
+ Display display = mInjector.getDisplay(Display.DEFAULT_DISPLAY);
if (display == null) {
return;
}
@@ -2753,8 +2740,7 @@ public class DisplayModeDirector {
sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this);
synchronized (mSensorObserverLock) {
- for (Display d : mDisplayManager.getDisplays(
- DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) {
+ for (Display d : mInjector.getDisplays()) {
mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d));
}
}
@@ -2765,8 +2751,7 @@ public class DisplayModeDirector {
}
private void recalculateVotesLocked() {
- final Display[] displays = mDisplayManager.getDisplays(
- DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
+ final Display[] displays = mInjector.getDisplays();
for (Display d : displays) {
int displayId = d.getDisplayId();
Vote vote = null;
@@ -2797,7 +2782,7 @@ public class DisplayModeDirector {
@Override
public void onDisplayAdded(int displayId) {
- boolean isDozeState = mInjector.isDozeState(mDisplayManager.getDisplay(displayId));
+ boolean isDozeState = mInjector.isDozeState(mInjector.getDisplay(displayId));
synchronized (mSensorObserverLock) {
mDozeStateByDisplay.put(displayId, isDozeState);
recalculateVotesLocked();
@@ -2809,7 +2794,7 @@ public class DisplayModeDirector {
boolean wasDozeState = mDozeStateByDisplay.get(displayId);
synchronized (mSensorObserverLock) {
mDozeStateByDisplay.put(displayId,
- mInjector.isDozeState(mDisplayManager.getDisplay(displayId)));
+ mInjector.isDozeState(mInjector.getDisplay(displayId)));
if (wasDozeState != mDozeStateByDisplay.get(displayId)) {
recalculateVotesLocked();
}
@@ -3165,17 +3150,27 @@ public class DisplayModeDirector {
}
interface Injector {
- Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
+ Uri SMOOTH_DISPLAY_URI = Settings.System.getUriFor(Settings.System.SMOOTH_DISPLAY);
+ Uri FORCE_PEAK_REFRESH_RATE_URI =
+ Settings.System.getUriFor(Settings.System.FORCE_PEAK_REFRESH_RATE);
@NonNull
DeviceConfigInterface getDeviceConfig();
- void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+ void registerSmoothDisplayObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer);
+ void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer);
+
+ void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+ Handler handler);
+
void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
Handler handler, long flags);
+ Display getDisplay(int displayId);
+
Display[] getDisplays();
boolean getDisplayInfo(int displayId, DisplayInfo displayInfo);
@@ -3205,19 +3200,37 @@ public class DisplayModeDirector {
}
@Override
- public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+ public void registerSmoothDisplayObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ cr.registerContentObserver(SMOOTH_DISPLAY_URI, false /*notifyDescendants*/,
+ observer, UserHandle.USER_SYSTEM);
+ }
+
+ @Override
+ public void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
- cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
+ cr.registerContentObserver(FORCE_PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
observer, UserHandle.USER_SYSTEM);
}
@Override
public void registerDisplayListener(DisplayManager.DisplayListener listener,
+ Handler handler) {
+ getDisplayManager().registerDisplayListener(listener, handler);
+ }
+
+ @Override
+ public void registerDisplayListener(DisplayManager.DisplayListener listener,
Handler handler, long flags) {
getDisplayManager().registerDisplayListener(listener, handler, flags);
}
@Override
+ public Display getDisplay(int displayId) {
+ return getDisplayManager().getDisplay(displayId);
+ }
+
+ @Override
public Display[] getDisplays() {
return getDisplayManager().getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
}
@@ -3225,10 +3238,13 @@ public class DisplayModeDirector {
@Override
public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) {
Display display = getDisplayManager().getDisplay(displayId);
- if (display != null) {
- return display.getDisplayInfo(displayInfo);
+ if (display == null) {
+ // We can occasionally get a display added or changed event for a display that was
+ // subsequently removed, which means this returns null. Check this case and bail
+ // out early; if it gets re-attached we'll eventually get another call back for it.
+ return false;
}
- return false;
+ return display.getDisplayInfo(displayInfo);
}
@Override
diff --git a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
index c04735d8f7e2..8a3b329211cf 100644
--- a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
+++ b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java
@@ -138,7 +138,7 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
for (Display d : displays) {
final int displayId = d.getDisplayId();
d.getDisplayInfo(info);
- localMap.put(displayId, info.refreshRateThermalThrottling);
+ localMap.put(displayId, info.thermalRefreshRateThrottling);
}
synchronized (mThermalObserverLock) {
for (int i = 0; i < size; i++) {
@@ -154,7 +154,7 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
DisplayInfo displayInfo = new DisplayInfo();
mInjector.getDisplayInfo(displayId, displayInfo);
SparseArray<SurfaceControl.RefreshRateRange> throttlingMap =
- displayInfo.refreshRateThermalThrottling;
+ displayInfo.thermalRefreshRateThrottling;
synchronized (mThermalObserverLock) {
mThermalThrottlingByDisplay.put(displayId, throttlingMap);
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index de10b1b19a33..6d70d21e3b84 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -345,6 +345,7 @@ final class DreamController {
if (!mCurrentDream.mIsPreviewMode && !mSentStartBroadcast) {
mContext.sendBroadcastAsUser(mDreamingStartedIntent, UserHandle.ALL,
null /* receiverPermission */, mDreamingStartedStoppedOptions);
+ mListener.onDreamStarted(mCurrentDream.mToken);
mSentStartBroadcast = true;
}
}
@@ -353,6 +354,7 @@ final class DreamController {
* Callback interface to be implemented by the {@link DreamManagerService}.
*/
public interface Listener {
+ void onDreamStarted(Binder token);
void onDreamStopped(Binder token);
}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 0e26d4661017..d2dcc508d01f 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -84,6 +84,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Consumer;
/**
* Service api for managing dreams.
@@ -341,10 +342,24 @@ public final class DreamManagerService extends SystemService {
}
private void reportKeepDreamingWhenUnpluggingChanged(boolean keepDreaming) {
+ notifyDreamStateListeners(
+ listener -> listener.onKeepDreamingWhenUnpluggingChanged(keepDreaming));
+ }
+
+ private void reportDreamingStarted() {
+ notifyDreamStateListeners(listener -> listener.onDreamingStarted());
+ }
+
+ private void reportDreamingStopped() {
+ notifyDreamStateListeners(listener -> listener.onDreamingStopped());
+ }
+
+ private void notifyDreamStateListeners(
+ Consumer<DreamManagerInternal.DreamManagerStateListener> notifier) {
mHandler.post(() -> {
for (DreamManagerInternal.DreamManagerStateListener listener
: mDreamManagerStateListeners) {
- listener.onKeepDreamingWhenUnpluggingChanged(keepDreaming);
+ notifier.accept(listener);
}
});
}
@@ -767,12 +782,23 @@ public final class DreamManagerService extends SystemService {
private final DreamController.Listener mControllerListener = new DreamController.Listener() {
@Override
+ public void onDreamStarted(Binder token) {
+ // Note that this event is distinct from DreamManagerService#startDreamLocked as it
+ // tracks the DreamService attach point from DreamController, closest to the broadcast
+ // of ACTION_DREAMING_STARTED.
+
+ reportDreamingStarted();
+ }
+
+ @Override
public void onDreamStopped(Binder token) {
synchronized (mLock) {
if (mCurrentDream != null && mCurrentDream.token == token) {
cleanupDreamLocked();
}
}
+
+ reportDreamingStopped();
}
};
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 73440b7f2eec..12fc263e1c8c 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -140,34 +140,39 @@ abstract public class ManagedServices {
* The services that have been bound by us. If the service is also connected, it will also
* be in {@link #mServices}.
*/
+ @GuardedBy("mMutex")
private final ArrayList<Pair<ComponentName, Integer>> mServicesBound = new ArrayList<>();
+ @GuardedBy("mMutex")
private final ArraySet<Pair<ComponentName, Integer>> mServicesRebinding = new ArraySet<>();
// we need these packages to be protected because classes that inherit from it need to see it
protected final Object mDefaultsLock = new Object();
+ @GuardedBy("mDefaultsLock")
protected final ArraySet<ComponentName> mDefaultComponents = new ArraySet<>();
+ @GuardedBy("mDefaultsLock")
protected final ArraySet<String> mDefaultPackages = new ArraySet<>();
// lists the component names of all enabled (and therefore potentially connected)
// app services for current profiles.
- private ArraySet<ComponentName> mEnabledServicesForCurrentProfiles
- = new ArraySet<>();
+ @GuardedBy("mMutex")
+ private final ArraySet<ComponentName> mEnabledServicesForCurrentProfiles = new ArraySet<>();
// Just the packages from mEnabledServicesForCurrentProfiles
- private ArraySet<String> mEnabledServicesPackageNames = new ArraySet<>();
+ @GuardedBy("mMutex")
+ private final ArraySet<String> mEnabledServicesPackageNames = new ArraySet<>();
// Per user id, list of enabled packages that have nevertheless asked not to be run
- private final android.util.SparseSetArray<ComponentName> mSnoozing =
- new android.util.SparseSetArray<>();
+ @GuardedBy("mSnoozing")
+ private final SparseSetArray<ComponentName> mSnoozing = new SparseSetArray<>();
// List of approved packages or components (by user, then by primary/secondary) that are
// allowed to be bound as managed services. A package or component appearing in this list does
// not mean that we are currently bound to said package/component.
+ @GuardedBy("mApproved")
protected final ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved =
new ArrayMap<>();
-
// List of packages or components (by user) that are configured to be enabled/disabled
// explicitly by the user
@GuardedBy("mApproved")
protected ArrayMap<Integer, ArraySet<String>> mUserSetServices = new ArrayMap<>();
-
+ @GuardedBy("mApproved")
protected ArrayMap<Integer, Boolean> mIsUserChanged = new ArrayMap<>();
// True if approved services are stored in xml, not settings.
@@ -262,20 +267,18 @@ abstract public class ManagedServices {
@NonNull
ArrayMap<Boolean, ArrayList<ComponentName>> resetComponents(String packageName, int userId) {
// components that we want to enable
- ArrayList<ComponentName> componentsToEnable =
- new ArrayList<>(mDefaultComponents.size());
-
+ ArrayList<ComponentName> componentsToEnable;
// components that were removed
- ArrayList<ComponentName> disabledComponents =
- new ArrayList<>(mDefaultComponents.size());
-
+ ArrayList<ComponentName> disabledComponents;
// all components that are enabled now
- ArraySet<ComponentName> enabledComponents =
- new ArraySet<>(getAllowedComponents(userId));
+ ArraySet<ComponentName> enabledComponents = new ArraySet<>(getAllowedComponents(userId));
boolean changed = false;
synchronized (mDefaultsLock) {
+ componentsToEnable = new ArrayList<>(mDefaultComponents.size());
+ disabledComponents = new ArrayList<>(mDefaultComponents.size());
+
// record all components that are enabled but should not be by default
for (int i = 0; i < mDefaultComponents.size() && enabledComponents.size() > 0; i++) {
ComponentName currentDefault = mDefaultComponents.valueAt(i);
@@ -374,14 +377,14 @@ abstract public class ManagedServices {
}
}
- pw.println(" All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size()
- + ") enabled for current profiles:");
- for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
- if (filter != null && !filter.matches(cmpt)) continue;
- pw.println(" " + cmpt);
- }
-
synchronized (mMutex) {
+ pw.println(" All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size()
+ + ") enabled for current profiles:");
+ for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
+ if (filter != null && !filter.matches(cmpt)) continue;
+ pw.println(" " + cmpt);
+ }
+
pw.println(" Live " + getCaption() + "s (" + mServices.size() + "):");
for (ManagedServiceInfo info : mServices) {
if (filter != null && !filter.matches(info.component)) continue;
@@ -434,12 +437,12 @@ abstract public class ManagedServices {
}
}
- for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
- if (filter != null && !filter.matches(cmpt)) continue;
- cmpt.dumpDebug(proto, ManagedServicesProto.ENABLED);
- }
synchronized (mMutex) {
+ for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
+ if (filter != null && !filter.matches(cmpt)) continue;
+ cmpt.dumpDebug(proto, ManagedServicesProto.ENABLED);
+ }
for (ManagedServiceInfo info : mServices) {
if (filter != null && !filter.matches(info.component)) continue;
info.dumpDebug(proto, ManagedServicesProto.LIVE_SERVICES, this);
@@ -677,7 +680,9 @@ abstract public class ManagedServices {
if (isUserChanged == null) { //NLS
userSetComponent = TextUtils.emptyIfNull(userSetComponent);
} else { //NAS
- mIsUserChanged.put(resolvedUserId, Boolean.valueOf(isUserChanged));
+ synchronized (mApproved) {
+ mIsUserChanged.put(resolvedUserId, Boolean.valueOf(isUserChanged));
+ }
userSetComponent = Boolean.valueOf(isUserChanged) ? approved : "";
}
} else {
@@ -688,7 +693,9 @@ abstract public class ManagedServices {
if (isUserChanged_Old != null && Boolean.valueOf(isUserChanged_Old)) {
//user_set = true
userSetComponent = approved;
- mIsUserChanged.put(resolvedUserId, true);
+ synchronized (mApproved) {
+ mIsUserChanged.put(resolvedUserId, true);
+ }
needUpgradeUserset = false;
} else {
userSetComponent = "";
@@ -724,8 +731,11 @@ abstract public class ManagedServices {
}
void upgradeDefaultsXmlVersion() {
- // check if any defaults are loaded
- int defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
+ int defaultsSize;
+ synchronized (mDefaultsLock) {
+ // check if any defaults are loaded
+ defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
+ }
if (defaultsSize == 0) {
// load defaults from current allowed
if (this.mApprovalLevel == APPROVAL_BY_COMPONENT) {
@@ -741,8 +751,10 @@ abstract public class ManagedServices {
}
}
}
+ synchronized (mDefaultsLock) {
+ defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
+ }
// if no defaults are loaded, then load from config
- defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
if (defaultsSize == 0) {
loadDefaultsFromConfig();
}
@@ -806,7 +818,9 @@ abstract public class ManagedServices {
}
protected boolean isComponentEnabledForPackage(String pkg) {
- return mEnabledServicesPackageNames.contains(pkg);
+ synchronized (mMutex) {
+ return mEnabledServicesPackageNames.contains(pkg);
+ }
}
protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId,
@@ -959,9 +973,13 @@ abstract public class ManagedServices {
}
public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uidList) {
- if (DEBUG) Slog.d(TAG, "onPackagesChanged removingPackage=" + removingPackage
- + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
- + " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames);
+ if (DEBUG) {
+ synchronized (mMutex) {
+ Slog.d(TAG, "onPackagesChanged removingPackage=" + removingPackage
+ + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
+ + " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames);
+ }
+ }
if (pkgList != null && (pkgList.length > 0)) {
boolean anyServicesInvolved = false;
@@ -975,7 +993,7 @@ abstract public class ManagedServices {
}
}
for (String pkgName : pkgList) {
- if (mEnabledServicesPackageNames.contains(pkgName)) {
+ if (isComponentEnabledForPackage(pkgName)) {
anyServicesInvolved = true;
}
if (uidList != null && uidList.length > 0) {
@@ -1299,9 +1317,11 @@ abstract public class ManagedServices {
}
final Set<ComponentName> add = new HashSet<>(userComponents);
- ArraySet<ComponentName> snoozed = mSnoozing.get(userId);
- if (snoozed != null) {
- add.removeAll(snoozed);
+ synchronized (mSnoozing) {
+ ArraySet<ComponentName> snoozed = mSnoozing.get(userId);
+ if (snoozed != null) {
+ add.removeAll(snoozed);
+ }
}
componentsToBind.put(userId, add);
@@ -1605,9 +1625,12 @@ abstract public class ManagedServices {
}
}
+ @VisibleForTesting
boolean isBound(ComponentName cn, int userId) {
final Pair<ComponentName, Integer> servicesBindingTag = Pair.create(cn, userId);
- return mServicesBound.contains(servicesBindingTag);
+ synchronized (mMutex) {
+ return mServicesBound.contains(servicesBindingTag);
+ }
}
protected boolean isBoundOrRebinding(final ComponentName cn, final int userId) {
@@ -1833,7 +1856,9 @@ abstract public class ManagedServices {
public boolean isEnabledForCurrentProfiles() {
if (this.isSystem) return true;
if (this.connection == null) return false;
- return mEnabledServicesForCurrentProfiles.contains(this.component);
+ synchronized (mMutex) {
+ return mEnabledServicesForCurrentProfiles.contains(this.component);
+ }
}
/**
@@ -1877,7 +1902,9 @@ abstract public class ManagedServices {
/** convenience method for looking in mEnabledServicesForCurrentProfiles */
public boolean isComponentEnabledForCurrentProfiles(ComponentName component) {
- return mEnabledServicesForCurrentProfiles.contains(component);
+ synchronized (mMutex) {
+ return mEnabledServicesForCurrentProfiles.contains(component);
+ }
}
public static class UserProfiles {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 07891f30abd0..d1d6f5f10bc6 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -693,6 +693,7 @@ public class NotificationManagerService extends SystemService {
private NotificationRecordLogger mNotificationRecordLogger;
private InstanceIdSequence mNotificationInstanceIdSequence;
private Set<String> mMsgPkgsAllowedAsConvos = new HashSet();
+ private String mDefaultSearchSelectorPkg;
// Broadcast intent receiver for notification permissions review-related intents
private ReviewNotificationPermissionsReceiver mReviewNotificationPermissionsReceiver;
@@ -2435,6 +2436,8 @@ public class NotificationManagerService extends SystemService {
mMsgPkgsAllowedAsConvos = Set.of(getStringArrayResource(
com.android.internal.R.array.config_notificationMsgPkgsAllowedAsConvos));
+ mDefaultSearchSelectorPkg = getContext().getString(getContext().getResources()
+ .getIdentifier("config_defaultSearchSelectorPackageName", "string", "android"));
mFlagResolver = flagResolver;
@@ -6934,7 +6937,12 @@ public class NotificationManagerService extends SystemService {
*/
private boolean canBeNonDismissible(ApplicationInfo ai, Notification notification) {
return notification.isMediaNotification() || isEnterpriseExempted(ai)
- || isCallNotification(ai.packageName, ai.uid, notification);
+ || isCallNotification(ai.packageName, ai.uid, notification)
+ || isDefaultSearchSelectorPackage(ai.packageName);
+ }
+
+ private boolean isDefaultSearchSelectorPackage(String pkg) {
+ return Objects.equals(mDefaultSearchSelectorPkg, pkg);
}
private boolean isEnterpriseExempted(ApplicationInfo ai) {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index f733199d9967..2460ce56f165 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -48,6 +48,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.metrics.LogMaker;
+import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.UserHandle;
@@ -60,6 +61,7 @@ import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
+import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -387,7 +389,8 @@ public class PreferencesHelper implements RankingConfig {
NotificationChannel channel = new NotificationChannel(
id, channelName, channelImportance);
if (forRestore) {
- channel.populateFromXmlForRestore(parser, mContext);
+ final boolean pkgInstalled = r.uid != UNKNOWN_UID;
+ channel.populateFromXmlForRestore(parser, pkgInstalled, mContext);
} else {
channel.populateFromXml(parser);
}
@@ -2412,6 +2415,21 @@ public class PreferencesHelper implements RankingConfig {
mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId));
synchronized (mPackagePreferences) {
mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
+
+ // Try to restore any unrestored sound resources
+ for (NotificationChannel channel : r.channels.values()) {
+ if (!channel.isSoundRestored()) {
+ Uri uri = channel.getSound();
+ Uri restoredUri = channel.restoreSoundUri(mContext, uri, true);
+ if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(
+ restoredUri)) {
+ Log.w(TAG,
+ "Could not restore sound: " + uri + " for channel: "
+ + channel);
+ }
+ channel.setSound(restoredUri, channel.getAudioAttributes());
+ }
+ }
}
if (r.migrateToPm) {
try {
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 2704f56b539d..02052808dd18 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -393,7 +393,7 @@ public final class BroadcastHelper {
public static boolean isPrivacySafetyLabelChangeNotificationsEnabled(Context context) {
PackageManager packageManager = context.getPackageManager();
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, false)
+ SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, true)
&& !packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
&& !packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
&& !packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH);
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 064be7c5ddc7..39cd88810961 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -745,6 +745,9 @@ public final class DexOptHelper {
applyPackageFilter(snapshot, remainingPredicate, result, remainingPkgSettings, sortTemp,
packageManagerService);
+ // Make sure the system server isn't in the result, because it can never be dexopted here.
+ result.removeIf(pkgSetting -> PLATFORM_PACKAGE_NAME.equals(pkgSetting.getPackageName()));
+
if (debug) {
Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgSettings));
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index eea6720d6cc4..ef7d41309e85 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -24,6 +24,7 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static com.android.server.LocalManagerRegistry.ManagerNotFoundException;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.accounts.IAccountManager;
import android.annotation.NonNull;
@@ -1954,6 +1955,8 @@ class PackageManagerShellCommand extends ShellCommand {
List<String> packageNames = null;
if (allPackages) {
packageNames = mInterface.getAllPackages();
+ // Compiling the system server is only supported from odrefresh, so skip it.
+ packageNames.removeIf(packageName -> PLATFORM_PACKAGE_NAME.equals(packageName));
} else {
String packageName = getNextArg();
if (packageName == null) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index fc6b4e9dcb20..0532a79fa8ef 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -710,7 +710,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final int deviceId = msg.arg1;
final Long eventTime = (Long) msg.obj;
launchAssistAction(null /* hint */, deviceId, eventTime,
- AssistUtils.INVOCATION_TYPE_UNKNOWN);
+ AssistUtils.INVOCATION_TYPE_ASSIST_BUTTON);
break;
case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK:
launchVoiceAssistWithWakeLock();
@@ -2186,12 +2186,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Intent.EXTRA_DOCK_STATE_UNDOCKED));
}
- // register for dream-related broadcasts
- filter = new IntentFilter();
- filter.addAction(Intent.ACTION_DREAMING_STARTED);
- filter.addAction(Intent.ACTION_DREAMING_STOPPED);
- mContext.registerReceiver(mDreamReceiver, filter);
-
// register for multiuser-relevant broadcasts
filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiver(mMultiuserReceiver, filter);
@@ -3484,6 +3478,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
}
return true;
+ case KeyEvent.KEYCODE_ESCAPE:
+ if (down && repeatCount == 0) {
+ mContext.closeSystemDialogs();
+ }
+ return true;
}
return false;
@@ -4571,6 +4570,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_BACK:
return mWakeOnBackKeyPress;
+
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL:
+ return mStylusButtonsEnabled;
}
return true;
@@ -4774,21 +4779,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
};
- BroadcastReceiver mDreamReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_DREAMING_STARTED.equals(intent.getAction())) {
- if (mKeyguardDelegate != null) {
- mKeyguardDelegate.onDreamingStarted();
- }
- } else if (Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())) {
- if (mKeyguardDelegate != null) {
- mKeyguardDelegate.onDreamingStopped();
- }
- }
- }
- };
-
BroadcastReceiver mMultiuserReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 646dc4e98c39..495e239d4cd7 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -18,6 +18,7 @@ import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.service.dreams.DreamManagerInternal;
import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -27,6 +28,7 @@ import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IKeyguardDrawnCallback;
import com.android.internal.policy.IKeyguardExitCallback;
import com.android.internal.policy.IKeyguardService;
+import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult;
import com.android.server.wm.EventLogTags;
@@ -60,6 +62,19 @@ public class KeyguardServiceDelegate {
private DrawnListener mDrawnListenerWhenConnect;
+ private final DreamManagerInternal.DreamManagerStateListener mDreamManagerStateListener =
+ new DreamManagerInternal.DreamManagerStateListener() {
+ @Override
+ public void onDreamingStarted() {
+ KeyguardServiceDelegate.this.onDreamingStarted();
+ }
+
+ @Override
+ public void onDreamingStopped() {
+ KeyguardServiceDelegate.this.onDreamingStopped();
+ }
+ };
+
private static final class KeyguardState {
KeyguardState() {
reset();
@@ -158,6 +173,11 @@ public class KeyguardServiceDelegate {
} else {
if (DEBUG) Log.v(TAG, "*** Keyguard started");
}
+
+ final DreamManagerInternal dreamManager =
+ LocalServices.getService(DreamManagerInternal.class);
+
+ dreamManager.registerDreamManagerStateListener(mDreamManagerStateListener);
}
private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
diff --git a/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
index 1d63489f3c4f..eb6d28e76ff8 100644
--- a/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
@@ -17,6 +17,8 @@
package com.android.server.power.stats.wakeups;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SENSOR;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
@@ -54,10 +56,11 @@ import java.util.regex.Pattern;
*/
public class CpuWakeupStats {
private static final String TAG = "CpuWakeupStats";
-
private static final String SUBSYSTEM_ALARM_STRING = "Alarm";
private static final String SUBSYSTEM_WIFI_STRING = "Wifi";
private static final String SUBSYSTEM_SOUND_TRIGGER_STRING = "Sound_trigger";
+ private static final String SUBSYSTEM_SENSOR_STRING = "Sensor";
+ private static final String SUBSYSTEM_CELLULAR_DATA_STRING = "Cellular_data";
private static final String TRACE_TRACK_WAKEUP_ATTRIBUTION = "wakeup_attribution";
@VisibleForTesting
static final long WAKEUP_REASON_HALF_WINDOW_MS = 500;
@@ -111,6 +114,10 @@ public class CpuWakeupStats {
return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__WIFI;
case CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER:
return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__SOUND_TRIGGER;
+ case CPU_WAKEUP_SUBSYSTEM_SENSOR:
+ return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__SENSOR;
+ case CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA:
+ return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__CELLULAR_DATA;
}
return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__UNKNOWN;
}
@@ -542,6 +549,10 @@ public class CpuWakeupStats {
return CPU_WAKEUP_SUBSYSTEM_WIFI;
case SUBSYSTEM_SOUND_TRIGGER_STRING:
return CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
+ case SUBSYSTEM_SENSOR_STRING:
+ return CPU_WAKEUP_SUBSYSTEM_SENSOR;
+ case SUBSYSTEM_CELLULAR_DATA_STRING:
+ return CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA;
}
return CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
}
@@ -554,6 +565,10 @@ public class CpuWakeupStats {
return SUBSYSTEM_WIFI_STRING;
case CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER:
return SUBSYSTEM_SOUND_TRIGGER_STRING;
+ case CPU_WAKEUP_SUBSYSTEM_SENSOR:
+ return SUBSYSTEM_SENSOR_STRING;
+ case CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA:
+ return SUBSYSTEM_CELLULAR_DATA_STRING;
case CPU_WAKEUP_SUBSYSTEM_UNKNOWN:
return "Unknown";
}
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 2007079ea5ca..0ca560398657 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -121,7 +121,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
} else if (getAvailableRollback(failedPackage) != null) {
// Rollback is available, we may get a callback into #execute
- impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_60;
} else if (anyRollbackAvailable) {
// If any rollbacks are available, we will commit them
impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
index cd1a968a8e89..97e463646fdc 100644
--- a/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
+++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningService.java
@@ -19,14 +19,18 @@ package com.android.server.security.rkp;
import android.content.Context;
import android.os.Binder;
import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.security.rkp.IGetRegistrationCallback;
import android.security.rkp.IRemoteProvisioning;
import android.security.rkp.service.RegistrationProxy;
import android.util.Log;
+import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.time.Duration;
import java.util.concurrent.Executor;
@@ -97,5 +101,18 @@ public class RemoteProvisioningService extends SystemService {
Binder.restoreCallingIdentity(callingIdentity);
}
}
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
+ new RemoteProvisioningShellCommand().dump(pw);
+ }
+
+ @Override
+ public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
+ ParcelFileDescriptor err, String[] args) {
+ return new RemoteProvisioningShellCommand().exec(this, in.getFileDescriptor(),
+ out.getFileDescriptor(), err.getFileDescriptor(), args);
+ }
}
}
diff --git a/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java b/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java
new file mode 100644
index 000000000000..71eca691dba7
--- /dev/null
+++ b/services/core/java/com/android/server/security/rkp/RemoteProvisioningShellCommand.java
@@ -0,0 +1,250 @@
+/*
+ * 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.server.security.rkp;
+
+import android.hardware.security.keymint.DeviceInfo;
+import android.hardware.security.keymint.IRemotelyProvisionedComponent;
+import android.hardware.security.keymint.MacedPublicKey;
+import android.hardware.security.keymint.ProtectedData;
+import android.hardware.security.keymint.RpcHardwareInfo;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ShellCommand;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.Base64;
+
+import co.nstant.in.cbor.CborDecoder;
+import co.nstant.in.cbor.CborEncoder;
+import co.nstant.in.cbor.CborException;
+import co.nstant.in.cbor.model.Array;
+import co.nstant.in.cbor.model.ByteString;
+import co.nstant.in.cbor.model.DataItem;
+import co.nstant.in.cbor.model.Map;
+import co.nstant.in.cbor.model.SimpleValue;
+import co.nstant.in.cbor.model.UnsignedInteger;
+
+class RemoteProvisioningShellCommand extends ShellCommand {
+ private static final String USAGE = "usage: cmd remote_provisioning SUBCOMMAND [ARGS]\n"
+ + "help\n"
+ + " Show this message.\n"
+ + "dump\n"
+ + " Dump service diagnostics.\n"
+ + "list [--min-version MIN_VERSION]\n"
+ + " List the names of the IRemotelyProvisionedComponent instances.\n"
+ + "csr [--challenge CHALLENGE] NAME\n"
+ + " Generate and print a base64-encoded CSR from the named\n"
+ + " IRemotelyProvisionedComponent. A base64-encoded challenge can be provided,\n"
+ + " or else it defaults to an empty challenge.\n";
+
+ @VisibleForTesting
+ static final String EEK_ED25519_BASE64 = "goRDoQEnoFgqpAEBAycgBiFYIJm57t1e5FL2hcZMYtw+YatXSH11N"
+ + "ymtdoAy0rPLY1jZWEAeIghLpLekyNdOAw7+uK8UTKc7b6XN3Np5xitk/pk5r3bngPpmAIUNB5gqrJFcpyUUS"
+ + "QY0dcqKJ3rZ41pJ6wIDhEOhASegWE6lAQECWCDQrsEVyirPc65rzMvRlh1l6LHd10oaN7lDOpfVmd+YCAM4G"
+ + "CAEIVggvoXnRsSjQlpA2TY6phXQLFh+PdwzAjLS/F4ehyVfcmBYQJvPkOIuS6vRGLEOjl0gJ0uEWP78MpB+c"
+ + "gWDvNeCvvpkeC1UEEvAMb9r6B414vAtzmwvT/L1T6XUg62WovGHWAQ=";
+
+ @VisibleForTesting
+ static final String EEK_P256_BASE64 = "goRDoQEmoFhNpQECAyYgASFYIPcUituX9MxT79JkEcTjdR9mH6RxDGzP"
+ + "+glGgHSHVPKtIlggXn9b9uzk9hnM/xM3/Q+hyJPbGAZ2xF3m12p3hsMtr49YQC+XjkL7vgctlUeFR5NAsB/U"
+ + "m0ekxESp8qEHhxDHn8sR9L+f6Dvg5zRMFfx7w34zBfTRNDztAgRgehXgedOK/ySEQ6EBJqBYcaYBAgJYIDVz"
+ + "tz+gioCJsSZn6ct8daGvAmH8bmUDkTvTS30UlD5GAzgYIAEhWCDgQc8vDzQPHDMsQbDP1wwwVTXSHmpHE0su"
+ + "0UiWfiScaCJYIB/ORcX7YbqBIfnlBZubOQ52hoZHuB4vRfHOr9o/gGjbWECMs7p+ID4ysGjfYNEdffCsOI5R"
+ + "vP9s4Wc7Snm8Vnizmdh8igfY2rW1f3H02GvfMyc0e2XRKuuGmZirOrSAqr1Q";
+
+ private static final int ERROR = -1;
+ private static final int SUCCESS = 0;
+
+ private final Injector mInjector;
+
+ RemoteProvisioningShellCommand() {
+ this(new Injector());
+ }
+
+ @VisibleForTesting
+ RemoteProvisioningShellCommand(Injector injector) {
+ mInjector = injector;
+ }
+
+ @Override
+ public void onHelp() {
+ getOutPrintWriter().print(USAGE);
+ }
+
+ @Override
+ @SuppressWarnings("CatchAndPrintStackTrace")
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ try {
+ switch (cmd) {
+ case "list":
+ return list();
+ case "csr":
+ return csr();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ } catch (Exception e) {
+ e.printStackTrace(getErrPrintWriter());
+ return ERROR;
+ }
+ }
+
+ @SuppressWarnings("CatchAndPrintStackTrace")
+ void dump(PrintWriter pw) {
+ try {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ for (String name : mInjector.getIrpcNames()) {
+ ipw.println(name + ":");
+ ipw.increaseIndent();
+ dumpRpcInstance(ipw, name);
+ ipw.decreaseIndent();
+ }
+ } catch (Exception e) {
+ e.printStackTrace(pw);
+ }
+ }
+
+ private void dumpRpcInstance(PrintWriter pw, String name) throws RemoteException {
+ RpcHardwareInfo info = mInjector.getIrpcBinder(name).getHardwareInfo();
+ pw.println("hwVersion=" + info.versionNumber);
+ pw.println("rpcAuthorName=" + info.rpcAuthorName);
+ if (info.versionNumber < 3) {
+ pw.println("supportedEekCurve=" + info.supportedEekCurve);
+ }
+ pw.println("uniqueId=" + info.uniqueId);
+ pw.println("supportedNumKeysInCsr=" + info.supportedNumKeysInCsr);
+ }
+
+ private int list() throws RemoteException {
+ for (String name : mInjector.getIrpcNames()) {
+ getOutPrintWriter().println(name);
+ }
+ return SUCCESS;
+ }
+
+ private int csr() throws RemoteException, CborException {
+ byte[] challenge = {};
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--challenge":
+ challenge = Base64.getDecoder().decode(getNextArgRequired());
+ break;
+ default:
+ getErrPrintWriter().println("error: unknown option");
+ return ERROR;
+ }
+ }
+ String name = getNextArgRequired();
+
+ IRemotelyProvisionedComponent binder = mInjector.getIrpcBinder(name);
+ RpcHardwareInfo info = binder.getHardwareInfo();
+ MacedPublicKey[] emptyKeys = new MacedPublicKey[] {};
+ byte[] csrBytes;
+ switch (info.versionNumber) {
+ case 1:
+ case 2:
+ DeviceInfo deviceInfo = new DeviceInfo();
+ ProtectedData protectedData = new ProtectedData();
+ byte[] eek = getEekChain(info.supportedEekCurve);
+ byte[] keysToSignMac = binder.generateCertificateRequest(
+ /*testMode=*/false, emptyKeys, eek, challenge, deviceInfo, protectedData);
+ csrBytes = composeCertificateRequestV1(
+ deviceInfo, challenge, protectedData, keysToSignMac);
+ break;
+ case 3:
+ csrBytes = binder.generateCertificateRequestV2(emptyKeys, challenge);
+ break;
+ default:
+ getErrPrintWriter().println("error: unsupported hwVersion: " + info.versionNumber);
+ return ERROR;
+ }
+ getOutPrintWriter().println(Base64.getEncoder().encodeToString(csrBytes));
+ return SUCCESS;
+ }
+
+ private byte[] getEekChain(int supportedEekCurve) {
+ switch (supportedEekCurve) {
+ case RpcHardwareInfo.CURVE_25519:
+ return Base64.getDecoder().decode(EEK_ED25519_BASE64);
+ case RpcHardwareInfo.CURVE_P256:
+ return Base64.getDecoder().decode(EEK_P256_BASE64);
+ default:
+ throw new IllegalArgumentException("unsupported EEK curve: " + supportedEekCurve);
+ }
+ }
+
+ private byte[] composeCertificateRequestV1(DeviceInfo deviceInfo, byte[] challenge,
+ ProtectedData protectedData, byte[] keysToSignMac) throws CborException {
+ Array info = new Array()
+ .add(decode(deviceInfo.deviceInfo))
+ .add(new Map());
+
+ // COSE_Signature with the hmac-sha256 algorithm and without a payload.
+ Array mac = new Array()
+ .add(new ByteString(encode(
+ new Map().put(new UnsignedInteger(1), new UnsignedInteger(5)))))
+ .add(new Map())
+ .add(SimpleValue.NULL)
+ .add(new ByteString(keysToSignMac));
+
+ Array csr = new Array()
+ .add(info)
+ .add(new ByteString(challenge))
+ .add(decode(protectedData.protectedData))
+ .add(mac);
+
+ return encode(csr);
+ }
+
+ private byte[] encode(DataItem item) throws CborException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ new CborEncoder(baos).encode(item);
+ return baos.toByteArray();
+ }
+
+ private DataItem decode(byte[] data) throws CborException {
+ ByteArrayInputStream bais = new ByteArrayInputStream(data);
+ return new CborDecoder(bais).decodeNext();
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ String[] getIrpcNames() {
+ return ServiceManager.getDeclaredInstances(IRemotelyProvisionedComponent.DESCRIPTOR);
+ }
+
+ IRemotelyProvisionedComponent getIrpcBinder(String name) {
+ String irpc = IRemotelyProvisionedComponent.DESCRIPTOR + "/" + name;
+ IRemotelyProvisionedComponent binder =
+ IRemotelyProvisionedComponent.Stub.asInterface(
+ ServiceManager.waitForDeclaredService(irpc));
+ if (binder == null) {
+ throw new IllegalArgumentException("failed to find " + irpc);
+ }
+ return binder;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 0ce17ded31e4..6eded1a14dbf 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -171,6 +171,8 @@ import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.StatsEvent;
import android.util.proto.ProtoOutputStream;
+import android.uwb.UwbActivityEnergyInfo;
+import android.uwb.UwbManager;
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
@@ -346,6 +348,7 @@ public class StatsPullAtomService extends SystemService {
private StorageManager mStorageManager;
private WifiManager mWifiManager;
private TelephonyManager mTelephony;
+ private UwbManager mUwbManager;
private SubscriptionManager mSubscriptionManager;
private NetworkStatsManager mNetworkStatsManager;
@@ -415,6 +418,7 @@ public class StatsPullAtomService extends SystemService {
private final Object mWifiActivityInfoLock = new Object();
private final Object mModemActivityInfoLock = new Object();
private final Object mBluetoothActivityInfoLock = new Object();
+ private final Object mUwbActivityInfoLock = new Object();
private final Object mSystemElapsedRealtimeLock = new Object();
private final Object mSystemUptimeLock = new Object();
private final Object mProcessMemoryStateLock = new Object();
@@ -537,6 +541,10 @@ public class StatsPullAtomService extends SystemService {
synchronized (mBluetoothActivityInfoLock) {
return pullBluetoothActivityInfoLocked(atomTag, data);
}
+ case FrameworkStatsLog.UWB_ACTIVITY_INFO:
+ synchronized (mUwbActivityInfoLock) {
+ return pullUwbActivityInfoLocked(atomTag, data);
+ }
case FrameworkStatsLog.SYSTEM_ELAPSED_REALTIME:
synchronized (mSystemElapsedRealtimeLock) {
return pullSystemElapsedRealtimeLocked(atomTag, data);
@@ -778,8 +786,12 @@ public class StatsPullAtomService extends SystemService {
registerEventListeners();
});
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
- // Network stats related pullers can only be initialized after service is ready.
- BackgroundThread.getHandler().post(() -> initAndRegisterNetworkStatsPullers());
+ BackgroundThread.getHandler().post(() -> {
+ // Network stats related pullers can only be initialized after service is ready.
+ initAndRegisterNetworkStatsPullers();
+ // For services that are not ready at boot phase PHASE_SYSTEM_SERVICES_READY
+ initAndRegisterDeferredPullers();
+ });
}
}
@@ -990,6 +1002,12 @@ public class StatsPullAtomService extends SystemService {
registerOemManagedBytesTransfer();
}
+ private void initAndRegisterDeferredPullers() {
+ mUwbManager = mContext.getSystemService(UwbManager.class);
+
+ registerUwbActivityInfo();
+ }
+
private IThermalService getIThermalService() {
synchronized (mThermalLock) {
if (mThermalService == null) {
@@ -2152,6 +2170,46 @@ public class StatsPullAtomService extends SystemService {
return StatsManager.PULL_SUCCESS;
}
+ private void registerUwbActivityInfo() {
+ int tagId = FrameworkStatsLog.UWB_ACTIVITY_INFO;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
+
+ int pullUwbActivityInfoLocked(int atomTag, List<StatsEvent> pulledData) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ SynchronousResultReceiver uwbReceiver = new SynchronousResultReceiver("uwb");
+ mUwbManager.getUwbActivityEnergyInfoAsync(Runnable::run,
+ info -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info);
+ uwbReceiver.send(0, bundle);
+ }
+ );
+ final UwbActivityEnergyInfo uwbInfo = awaitControllerInfo(uwbReceiver);
+ if (uwbInfo == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(atomTag,
+ uwbInfo.getControllerTxDurationMillis(),
+ uwbInfo.getControllerRxDurationMillis(),
+ uwbInfo.getControllerIdleDurationMillis(),
+ uwbInfo.getControllerWakeCount()));
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "failed to getUwbActivityEnergyInfoAsync", e);
+ return StatsManager.PULL_SKIP;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+
private void registerSystemElapsedRealtime() {
int tagId = FrameworkStatsLog.SYSTEM_ELAPSED_REALTIME;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index e17866922990..51872b339c40 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1229,10 +1229,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return;
}
+ // Live wallpapers always are system wallpapers unless lock screen live wp is
+ // enabled.
+ which = mIsLockscreenLiveWallpaperEnabled ? mWallpaper.mWhich : FLAG_SYSTEM;
mWallpaper.primaryColors = primaryColors;
- // Live wallpapers always are system wallpapers.
- which = FLAG_SYSTEM;
// It's also the lock screen wallpaper when we don't have a bitmap in there.
if (displayId == DEFAULT_DISPLAY) {
final WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index d844c6f17a5d..9647a620b6c8 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -84,6 +84,7 @@ public interface ActivityInterceptorCallback {
PERMISSION_POLICY_ORDERED_ID,
VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
DREAM_MANAGER_ORDERED_ID,
+ PRODUCT_ORDERED_ID,
SYSTEM_LAST_ORDERED_ID, // Update this when adding new ids
// Order Ids for mainline module services
MAINLINE_FIRST_ORDERED_ID,
@@ -119,11 +120,18 @@ public interface ActivityInterceptorCallback {
int DREAM_MANAGER_ORDERED_ID = 4;
/**
+ * The identifier for an interceptor which is specific to the type of android product like
+ * automotive, wear, TV etc.
+ * @hide
+ */
+ int PRODUCT_ORDERED_ID = 5;
+
+ /**
* The final id, used by the framework to determine the valid range of ids. Update this when
* adding new ids.
* @hide
*/
- int SYSTEM_LAST_ORDERED_ID = DREAM_MANAGER_ORDERED_ID;
+ int SYSTEM_LAST_ORDERED_ID = PRODUCT_ORDERED_ID;
/**
* The first mainline module id, used by the framework to determine the valid range of ids
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f93afe81f804..ea0731ae346a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1996,6 +1996,24 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
+ @Override
+ public void focusTopTask(int displayId) {
+ enforceTaskPermission("focusTopTask()");
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId);
+ if (dc == null) return;
+ final Task task = dc.getTask((t) -> t.isLeafTask() && t.isFocusable(),
+ true /* traverseTopToBottom */);
+ if (task == null) return;
+ setFocusedTask(task.mTaskId, null /* touchedActivity */);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
void setFocusedTask(int taskId, ActivityRecord touchedActivity) {
ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedTask: taskId=%d touchedActivity=%s", taskId,
touchedActivity);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index be503fc61c4c..7d220df949e0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -82,6 +82,7 @@ import static com.android.server.wm.Task.TAG_CLEANUP;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
@@ -261,6 +262,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
/** Helper for {@link Task#fillTaskInfo}. */
final TaskInfoHelper mTaskInfoHelper = new TaskInfoHelper();
+ final OpaqueActivityHelper mOpaqueActivityHelper = new OpaqueActivityHelper();
+
private final ActivityTaskSupervisorHandler mHandler;
final Looper mLooper;
@@ -2906,6 +2909,38 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
}
+ /** The helper to get the top opaque activity of a container. */
+ static class OpaqueActivityHelper implements Predicate<ActivityRecord> {
+ private ActivityRecord mStarting;
+ private boolean mIncludeInvisibleAndFinishing;
+
+ ActivityRecord getOpaqueActivity(@NonNull WindowContainer<?> container) {
+ mIncludeInvisibleAndFinishing = true;
+ return container.getActivity(this,
+ true /* traverseTopToBottom */, null /* boundary */);
+ }
+
+ ActivityRecord getVisibleOpaqueActivity(@NonNull WindowContainer<?> container,
+ @Nullable ActivityRecord starting) {
+ mStarting = starting;
+ mIncludeInvisibleAndFinishing = false;
+ final ActivityRecord opaque = container.getActivity(this,
+ true /* traverseTopToBottom */, null /* boundary */);
+ mStarting = null;
+ return opaque;
+ }
+
+ @Override
+ public boolean test(ActivityRecord r) {
+ if (!mIncludeInvisibleAndFinishing && !r.visibleIgnoringKeyguard && r != mStarting) {
+ // Ignore invisible activities that are not the currently starting activity
+ // (about to be visible).
+ return false;
+ }
+ return r.occludesParent(mIncludeInvisibleAndFinishing /* includingFinishing */);
+ }
+ }
+
/**
* Fills the info that needs to iterate all activities of task, such as the number of
* non-finishing activities and collecting launch cookies.
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index d916a1be1a03..7ecc0839fe53 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -133,7 +133,7 @@ class BLASTSyncEngine {
return mOrphanTransaction;
}
- private void onSurfacePlacement() {
+ private void tryFinish() {
if (!mReady) return;
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: onSurfacePlacement checking %s",
mSyncId, mRootMembers);
@@ -168,14 +168,13 @@ class BLASTSyncEngine {
class CommitCallback implements Runnable {
// Can run a second time if the action completes after the timeout.
boolean ran = false;
- public void onCommitted() {
+ public void onCommitted(SurfaceControl.Transaction t) {
synchronized (mWm.mGlobalLock) {
if (ran) {
return;
}
mHandler.removeCallbacks(this);
ran = true;
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
for (WindowContainer wc : wcAwaitingCommit) {
wc.onSyncTransactionCommitted(t);
}
@@ -194,12 +193,12 @@ class BLASTSyncEngine {
Slog.e(TAG, "WM sent Transaction to organized, but never received" +
" commit callback. Application ANR likely to follow.");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- onCommitted();
-
+ onCommitted(merged);
}
};
CommitCallback callback = new CommitCallback();
- merged.addTransactionCommittedListener((r) -> { r.run(); }, callback::onCommitted);
+ merged.addTransactionCommittedListener(Runnable::run,
+ () -> callback.onCommitted(new SurfaceControl.Transaction()));
mHandler.postDelayed(callback, BLAST_TIMEOUT_DURATION);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionReady");
@@ -223,6 +222,12 @@ class BLASTSyncEngine {
}
});
}
+ // Notify idle listeners
+ for (int i = mOnIdleListeners.size() - 1; i >= 0; --i) {
+ // If an idle listener adds a sync, though, then stop notifying.
+ if (mActiveSyncs.size() > 0) break;
+ mOnIdleListeners.get(i).run();
+ }
}
private void setReady(boolean ready) {
@@ -281,6 +286,8 @@ class BLASTSyncEngine {
*/
private final ArrayList<PendingSyncSet> mPendingSyncSets = new ArrayList<>();
+ private final ArrayList<Runnable> mOnIdleListeners = new ArrayList<>();
+
BLASTSyncEngine(WindowManagerService wms) {
this(wms, wms.mH);
}
@@ -379,10 +386,15 @@ class BLASTSyncEngine {
void onSurfacePlacement() {
// backwards since each state can remove itself if finished
for (int i = mActiveSyncs.size() - 1; i >= 0; --i) {
- mActiveSyncs.valueAt(i).onSurfacePlacement();
+ mActiveSyncs.valueAt(i).tryFinish();
}
}
+ /** Only use this for tests! */
+ void tryFinishForTest(int syncId) {
+ getSyncSet(syncId).tryFinish();
+ }
+
/**
* Queues a sync operation onto this engine. It will wait until any current/prior sync-sets
* have finished to run. This is needed right now because currently {@link BLASTSyncEngine}
@@ -409,4 +421,8 @@ class BLASTSyncEngine {
boolean hasPendingSyncSets() {
return !mPendingSyncSets.isEmpty();
}
+
+ void addOnIdleListener(Runnable onIdleListener) {
+ mOnIdleListeners.add(onIdleListener);
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index bad64d357b13..76d6951c438e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1858,7 +1858,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return false;
}
if (mLastWallpaperVisible && r.windowsCanBeWallpaperTarget()
- && mFixedRotationTransitionListener.mAnimatingRecents == null) {
+ && mFixedRotationTransitionListener.mAnimatingRecents == null
+ && !mTransitionController.isTransientLaunch(r)) {
// Use normal rotation animation for orientation change of visible wallpaper if recents
// animation is not running (it may be swiping to home).
return false;
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 3d006868c79c..35513707ebc5 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.view.SurfaceControl.HIDDEN;
+import static android.window.TaskConstants.TASK_CHILD_LAYER_LETTERBOX_BACKGROUND;
import android.content.Context;
import android.graphics.Color;
@@ -361,7 +362,8 @@ public class Letterbox {
.setCallsite("LetterboxSurface.createSurface")
.build();
- t.setLayer(mSurface, -1).setColorSpaceAgnostic(mSurface, true);
+ t.setLayer(mSurface, TASK_CHILD_LAYER_LETTERBOX_BACKGROUND)
+ .setColorSpaceAgnostic(mSurface, true);
}
void attachInput(WindowState win) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 5c33e6470024..99d3cc0737d1 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5652,7 +5652,7 @@ class Task extends TaskFragment {
(deferred) -> {
// Need to check again if deferred since the system might
// be in a different state.
- if (deferred && !canMoveTaskToBack(tr)) {
+ if (!isAttached() || (deferred && !canMoveTaskToBack(tr))) {
Slog.e(TAG, "Failed to move task to back after saying we could: "
+ tr.mTaskId);
transition.abort();
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 311b9a6d2876..3f7ab14d02be 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -102,8 +102,6 @@ import android.window.TaskFragmentOrganizerToken;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.am.HostingRecord;
import com.android.server.pm.pkg.AndroidPackage;
@@ -934,11 +932,10 @@ class TaskFragment extends WindowContainer<WindowContainer> {
if (!isAttached() || isForceHidden() || isForceTranslucent()) {
return true;
}
- final PooledPredicate p = PooledLambda.obtainPredicate(TaskFragment::isOpaqueActivity,
- PooledLambda.__(ActivityRecord.class), starting, false /* including*/);
- final ActivityRecord opaque = getActivity(p);
- p.recycle();
- return opaque == null;
+ // A TaskFragment isn't translucent if it has at least one visible activity that occludes
+ // this TaskFragment.
+ return mTaskSupervisor.mOpaqueActivityHelper.getVisibleOpaqueActivity(this,
+ starting) == null;
}
/**
@@ -951,25 +948,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return true;
}
// Including finishing Activity if the TaskFragment is becoming invisible in the transition.
- final boolean includingFinishing = !isVisibleRequested();
- final PooledPredicate p = PooledLambda.obtainPredicate(TaskFragment::isOpaqueActivity,
- PooledLambda.__(ActivityRecord.class), null /* starting */, includingFinishing);
- final ActivityRecord opaque = getActivity(p);
- p.recycle();
- return opaque == null;
- }
-
- private static boolean isOpaqueActivity(@NonNull ActivityRecord r,
- @Nullable ActivityRecord starting, boolean includingFinishing) {
- if (!r.visibleIgnoringKeyguard && r != starting) {
- // Also ignore invisible activities that are not the currently starting
- // activity (about to be visible).
- return false;
- }
-
- // TaskFragment isn't translucent if it has at least one fullscreen activity that is
- // visible.
- return r.occludesParent(includingFinishing);
+ return mTaskSupervisor.mOpaqueActivityHelper.getOpaqueActivity(this) == null;
}
ActivityRecord getTopNonFinishingActivity() {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 76b0e7b82ba6..452bd6d7b347 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -104,7 +104,8 @@ import java.util.Objects;
import java.util.function.Predicate;
/**
- * Represents a logical transition.
+ * Represents a logical transition. This keeps track of all the changes associated with a logical
+ * WM state -> state transition.
* @see TransitionController
*/
class Transition implements BLASTSyncEngine.TransactionReadyListener {
@@ -416,6 +417,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
return mFinishTransaction;
}
+ boolean isPending() {
+ return mState == STATE_PENDING;
+ }
+
boolean isCollecting() {
return mState == STATE_COLLECTING || mState == STATE_STARTED;
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index cbb4fe2eaa21..e8e4792690be 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -51,6 +51,7 @@ import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.FgThread;
@@ -88,6 +89,7 @@ class TransitionController {
private WindowProcessController mTransitionPlayerProc;
final ActivityTaskManagerService mAtm;
+ BLASTSyncEngine mSyncEngine;
final RemotePlayer mRemotePlayer;
SnapshotController mSnapshotController;
@@ -121,6 +123,26 @@ class TransitionController {
private final IBinder.DeathRecipient mTransitionPlayerDeath;
+ static class QueuedTransition {
+ final Transition mTransition;
+ final OnStartCollect mOnStartCollect;
+ final BLASTSyncEngine.SyncGroup mLegacySync;
+
+ QueuedTransition(Transition transition, OnStartCollect onStartCollect) {
+ mTransition = transition;
+ mOnStartCollect = onStartCollect;
+ mLegacySync = null;
+ }
+
+ QueuedTransition(BLASTSyncEngine.SyncGroup legacySync, OnStartCollect onStartCollect) {
+ mTransition = null;
+ mOnStartCollect = onStartCollect;
+ mLegacySync = legacySync;
+ }
+ }
+
+ private final ArrayList<QueuedTransition> mQueuedTransitions = new ArrayList<>();
+
/** The transition currently being constructed (collecting participants). */
private Transition mCollectingTransition = null;
@@ -158,10 +180,20 @@ class TransitionController {
mTransitionTracer = wms.mTransitionTracer;
mIsWaitingForDisplayEnabled = !wms.mDisplayEnabled;
registerLegacyListener(wms.mActivityManagerAppTransitionNotifier);
+ setSyncEngine(wms.mSyncEngine);
+ }
+
+ @VisibleForTesting
+ void setSyncEngine(BLASTSyncEngine syncEngine) {
+ mSyncEngine = syncEngine;
+ // Check the queue whenever the sync-engine becomes idle.
+ mSyncEngine.addOnIdleListener(this::tryStartCollectFromQueue);
}
private void detachPlayer() {
if (mTransitionPlayer == null) return;
+ // Immediately set to null so that nothing inadvertently starts/queues.
+ mTransitionPlayer = null;
// Clean-up/finish any playing transitions.
for (int i = 0; i < mPlayingTransitions.size(); ++i) {
mPlayingTransitions.get(i).cleanUpOnFailure();
@@ -170,7 +202,6 @@ class TransitionController {
if (mCollectingTransition != null) {
mCollectingTransition.abort();
}
- mTransitionPlayer = null;
mTransitionPlayerProc = null;
mRemotePlayer.clear();
mRunningLock.doNotifyLocked();
@@ -195,7 +226,7 @@ class TransitionController {
throw new IllegalStateException("Simultaneous transition collection not supported"
+ " yet. Use {@link #createPendingTransition} for explicit queueing.");
}
- Transition transit = new Transition(type, flags, this, mAtm.mWindowManager.mSyncEngine);
+ Transition transit = new Transition(type, flags, this, mSyncEngine);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Creating Transition: %s", transit);
moveToCollecting(transit);
return transit;
@@ -325,7 +356,7 @@ class TransitionController {
/** @return {@code true} if a transition is running */
boolean inTransition() {
// TODO(shell-transitions): eventually properly support multiple
- return isCollecting() || isPlaying();
+ return isCollecting() || isPlaying() || !mQueuedTransitions.isEmpty();
}
/** @return {@code true} if a transition is running in a participant subtree of wc */
@@ -453,8 +484,7 @@ class TransitionController {
// some frames before and after the display projection transaction is applied by the
// remote player. That may cause some buffers to show in different rotation. So use
// sync method to pause clients drawing until the projection transaction is applied.
- mAtm.mWindowManager.mSyncEngine.setSyncMethod(displayTransition.getSyncId(),
- BLASTSyncEngine.METHOD_BLAST);
+ mSyncEngine.setSyncMethod(displayTransition.getSyncId(), BLASTSyncEngine.METHOD_BLAST);
}
final Rect startBounds = displayChange.getStartAbsBounds();
final Rect endBounds = displayChange.getEndAbsBounds();
@@ -741,6 +771,31 @@ class TransitionController {
mStateValidators.clear();
}
+ void tryStartCollectFromQueue() {
+ if (mQueuedTransitions.isEmpty()) return;
+ // Only need to try the next one since, even when transition can collect in parallel,
+ // they still need to serialize on readiness.
+ final QueuedTransition queued = mQueuedTransitions.get(0);
+ if (mCollectingTransition != null || mSyncEngine.hasActiveSync()) {
+ return;
+ }
+ mQueuedTransitions.remove(0);
+ // This needs to happen immediately to prevent another sync from claiming the syncset
+ // out-of-order (moveToCollecting calls startSyncSet)
+ if (queued.mTransition != null) {
+ moveToCollecting(queued.mTransition);
+ } else {
+ // legacy sync
+ mSyncEngine.startSyncSet(queued.mLegacySync);
+ }
+ // Post this so that the now-playing transition logic isn't interrupted.
+ mAtm.mH.post(() -> {
+ synchronized (mAtm.mGlobalLock) {
+ queued.mOnStartCollect.onCollectStarted(true /* deferred */);
+ }
+ });
+ }
+
void moveToPlaying(Transition transition) {
if (transition != mCollectingTransition) {
throw new IllegalStateException("Trying to move non-collecting transition to playing");
@@ -749,6 +804,7 @@ class TransitionController {
mPlayingTransitions.add(transition);
updateRunningRemoteAnimation(transition, true /* isPlaying */);
mTransitionTracer.logState(transition);
+ // Sync engine should become idle after this, so the idle listener will check the queue.
}
void updateAnimatingState(SurfaceControl.Transaction t) {
@@ -758,12 +814,12 @@ class TransitionController {
t.setEarlyWakeupStart();
// Usually transitions put quite a load onto the system already (with all the things
// happening in app), so pause task snapshot persisting to not increase the load.
- mAtm.mWindowManager.mSnapshotController.setPause(true);
+ mSnapshotController.setPause(true);
mAnimatingState = true;
Transition.asyncTraceBegin("animating", 0x41bfaf1 /* hashcode of TAG */);
} else if (!animatingState && mAnimatingState) {
t.setEarlyWakeupEnd();
- mAtm.mWindowManager.mSnapshotController.setPause(false);
+ mSnapshotController.setPause(false);
mAnimatingState = false;
Transition.asyncTraceEnd(0x41bfaf1 /* hashcode of TAG */);
}
@@ -793,6 +849,7 @@ class TransitionController {
transition.abort();
mCollectingTransition = null;
mTransitionTracer.logState(transition);
+ // abort will call through the normal finish paths and thus check the queue.
}
/**
@@ -874,7 +931,7 @@ class TransitionController {
if (!mPlayingTransitions.isEmpty()) {
state = LEGACY_STATE_RUNNING;
} else if ((mCollectingTransition != null && mCollectingTransition.getLegacyIsReady())
- || mAtm.mWindowManager.mSyncEngine.hasPendingSyncSets()) {
+ || mSyncEngine.hasPendingSyncSets()) {
// The transition may not be "ready", but we have a sync-transaction waiting to start.
// Usually the pending transaction is for a transition, so assuming that is the case,
// we can't be IDLE for test purposes. Ideally, we should have a STATE_COLLECTING.
@@ -885,25 +942,43 @@ class TransitionController {
}
/** Returns {@code true} if it started collecting, {@code false} if it was queued. */
+ private void queueTransition(Transition transit, OnStartCollect onStartCollect) {
+ mQueuedTransitions.add(new QueuedTransition(transit, onStartCollect));
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN,
+ "Queueing transition: %s", transit);
+ }
+
+ /** Returns {@code true} if it started collecting, {@code false} if it was queued. */
boolean startCollectOrQueue(Transition transit, OnStartCollect onStartCollect) {
- if (mAtm.mWindowManager.mSyncEngine.hasActiveSync()) {
+ if (!mQueuedTransitions.isEmpty()) {
+ // Just add to queue since we already have a queue.
+ queueTransition(transit, onStartCollect);
+ return false;
+ }
+ if (mSyncEngine.hasActiveSync()) {
if (!isCollecting()) {
Slog.w(TAG, "Ongoing Sync outside of transition.");
}
+ queueTransition(transit, onStartCollect);
+ return false;
+ }
+ moveToCollecting(transit);
+ onStartCollect.onCollectStarted(false /* deferred */);
+ return true;
+ }
+
+ /** Returns {@code true} if it started collecting, {@code false} if it was queued. */
+ boolean startLegacySyncOrQueue(BLASTSyncEngine.SyncGroup syncGroup, Runnable applySync) {
+ if (!mQueuedTransitions.isEmpty() || mSyncEngine.hasActiveSync()) {
+ // Just add to queue since we already have a queue.
+ mQueuedTransitions.add(new QueuedTransition(syncGroup, (d) -> applySync.run()));
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN,
- "Queueing transition: %s", transit);
- mAtm.mWindowManager.mSyncEngine.queueSyncSet(
- // Make sure to collect immediately to prevent another transition
- // from sneaking in before it. Note: moveToCollecting internally
- // calls startSyncSet.
- () -> moveToCollecting(transit),
- () -> onStartCollect.onCollectStarted(true /* deferred */));
+ "Queueing legacy sync-set: %s", syncGroup.mSyncId);
return false;
- } else {
- moveToCollecting(transit);
- onStartCollect.onCollectStarted(false /* deferred */);
- return true;
}
+ mSyncEngine.startSyncSet(syncGroup);
+ applySync.run();
+ return true;
}
interface OnStartCollect {
diff --git a/services/core/java/com/android/server/wm/TransitionTracer.java b/services/core/java/com/android/server/wm/TransitionTracer.java
index a4c931c17a66..6597d4c7f916 100644
--- a/services/core/java/com/android/server/wm/TransitionTracer.java
+++ b/services/core/java/com/android/server/wm/TransitionTracer.java
@@ -154,6 +154,7 @@ public class TransitionTracer {
}
outputStream.write(com.android.server.wm.shell.Transition.TYPE, transition.mType);
+ outputStream.write(com.android.server.wm.shell.Transition.FLAGS, transition.getFlags());
for (int i = 0; i < targets.size(); ++i) {
final long changeToken = outputStream
@@ -162,6 +163,7 @@ public class TransitionTracer {
final Transition.ChangeInfo target = targets.get(i);
final int mode = target.getTransitMode(target.mContainer);
+ final int flags = target.getChangeFlags(target.mContainer);
final int layerId;
if (target.mContainer.mSurfaceControl.isValid()) {
layerId = target.mContainer.mSurfaceControl.getLayerId();
@@ -170,6 +172,7 @@ public class TransitionTracer {
}
outputStream.write(com.android.server.wm.shell.Target.MODE, mode);
+ outputStream.write(com.android.server.wm.shell.Target.FLAGS, flags);
outputStream.write(com.android.server.wm.shell.Target.LAYER_ID, layerId);
if (mActiveTracingEnabled) {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 6c38c6fb7ac4..1ffee05d20ec 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -129,26 +129,10 @@ class WallpaperWindowToken extends WindowToken {
}
void updateWallpaperWindows(boolean visible) {
- boolean changed = false;
if (mVisibleRequested != visible) {
ProtoLog.d(WM_DEBUG_WALLPAPER, "Wallpaper token %s visible=%b",
token, visible);
setVisibility(visible);
- changed = true;
- }
- if (mTransitionController.isShellTransitionsEnabled()) {
- // Apply legacy fixed rotation to wallpaper if it is becoming visible
- if (!mTransitionController.useShellTransitionsRotation() && changed && visible) {
- final WindowState wallpaperTarget =
- mDisplayContent.mWallpaperController.getWallpaperTarget();
- if (wallpaperTarget != null && wallpaperTarget.mToken.hasFixedRotationTransform()) {
- linkFixedRotationTransform(wallpaperTarget.mToken);
- }
- }
- // If wallpaper is in transition, setVisible() will be called from commitVisibility()
- // when finishing transition. Otherwise commitVisibility() is already called from above
- // setVisibility().
- return;
}
final WindowState wallpaperTarget =
@@ -172,6 +156,12 @@ class WallpaperWindowToken extends WindowToken {
linkFixedRotationTransform(wallpaperTarget.mToken);
}
}
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ // If wallpaper is in transition, setVisible() will be called from commitVisibility()
+ // when finishing transition. Otherwise commitVisibility() is already called from above
+ // setVisibility().
+ return;
+ }
setVisible(visible);
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index ee86b97e9404..cd42528ad79b 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -231,19 +231,26 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
*/
final BLASTSyncEngine.SyncGroup syncGroup = prepareSyncWithOrganizer(callback);
final int syncId = syncGroup.mSyncId;
- if (!mService.mWindowManager.mSyncEngine.hasActiveSync()) {
- mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup);
- applyTransaction(t, syncId, null /*transition*/, caller);
- setSyncReady(syncId);
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ mTransitionController.startLegacySyncOrQueue(syncGroup, () -> {
+ applyTransaction(t, syncId, null /*transition*/, caller);
+ setSyncReady(syncId);
+ });
} else {
- // Because the BLAST engine only supports one sync at a time, queue the
- // transaction.
- mService.mWindowManager.mSyncEngine.queueSyncSet(
- () -> mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup),
- () -> {
- applyTransaction(t, syncId, null /*transition*/, caller);
- setSyncReady(syncId);
- });
+ if (!mService.mWindowManager.mSyncEngine.hasActiveSync()) {
+ mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup);
+ applyTransaction(t, syncId, null /*transition*/, caller);
+ setSyncReady(syncId);
+ } else {
+ // Because the BLAST engine only supports one sync at a time, queue the
+ // transaction.
+ mService.mWindowManager.mSyncEngine.queueSyncSet(
+ () -> mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup),
+ () -> {
+ applyTransaction(t, syncId, null /*transition*/, caller);
+ setSyncReady(syncId);
+ });
+ }
}
return syncId;
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 85c601fe0a5c..dbd9e4b8ea68 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -107,6 +107,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
+ private static final int MAX_RAPID_ACTIVITY_LAUNCH_COUNT = 500;
+ private static final long RAPID_ACTIVITY_LAUNCH_MS = 300;
+ private static final long RESET_RAPID_ACTIVITY_LAUNCH_MS = 5 * RAPID_ACTIVITY_LAUNCH_MS;
+
+ private int mRapidActivityLaunchCount;
+
// all about the first app in the process
final ApplicationInfo mInfo;
final String mName;
@@ -538,7 +544,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return mLastActivityLaunchTime > 0;
}
- void setLastActivityLaunchTime(long launchTime) {
+ void setLastActivityLaunchTime(ActivityRecord r) {
+ long launchTime = r.lastLaunchTime;
if (launchTime <= mLastActivityLaunchTime) {
if (launchTime < mLastActivityLaunchTime) {
Slog.w(TAG,
@@ -547,9 +554,29 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
return;
}
+ updateRapidActivityLaunch(r, launchTime, mLastActivityLaunchTime);
mLastActivityLaunchTime = launchTime;
}
+ void updateRapidActivityLaunch(ActivityRecord r, long launchTime, long lastLaunchTime) {
+ if (mInstrumenting || mDebugging || lastLaunchTime <= 0) {
+ return;
+ }
+
+ final long diff = lastLaunchTime - launchTime;
+ if (diff < RAPID_ACTIVITY_LAUNCH_MS) {
+ mRapidActivityLaunchCount++;
+ } else if (diff >= RESET_RAPID_ACTIVITY_LAUNCH_MS) {
+ mRapidActivityLaunchCount = 0;
+ }
+
+ if (mRapidActivityLaunchCount > MAX_RAPID_ACTIVITY_LAUNCH_COUNT) {
+ Slog.w(TAG, "Killing " + mPid + " because of rapid activity launch");
+ r.getRootTask().moveTaskToBack(r.getTask());
+ mAtm.mH.post(() -> mAtm.mAmInternal.killProcess(mName, mUid, "rapidActivityLaunch"));
+ }
+ }
+
void setLastActivityFinishTimeIfNeeded(long finishTime) {
if (finishTime <= mLastActivityFinishTime || !hasActivityInVisibleTask()) {
return;
@@ -696,7 +723,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
void addActivityIfNeeded(ActivityRecord r) {
// even if we already track this activity, note down that it has been launched
- setLastActivityLaunchTime(r.lastLaunchTime);
+ setLastActivityLaunchTime(r);
if (mActivities.contains(r)) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a29959297dc7..f3b338294393 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5634,7 +5634,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private void dropBufferFrom(Transaction t) {
SurfaceControl viewSurface = getClientViewRootSurface();
if (viewSurface == null) return;
- t.setBuffer(viewSurface, (android.hardware.HardwareBuffer) null);
+ t.unsetBuffer(viewSurface);
}
@Override
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 2584b86f53db..d9acf4182736 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -27,3 +27,4 @@ per-file com_android_server_security_* = file:/core/java/android/security/OWNERS
per-file com_android_server_tv_* = file:/media/java/android/media/tv/OWNERS
per-file com_android_server_vibrator_* = file:/services/core/java/com/android/server/vibrator/OWNERS
per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com
+per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
index 4898d95b8e4b..ad098b757eae 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -233,44 +233,50 @@ static void nativeCloseUinput(JNIEnv* env, jobject thiz, jlong ptr) {
// Native methods for VirtualDpad
static bool nativeWriteDpadKeyEvent(JNIEnv* env, jobject thiz, jlong ptr, jint androidKeyCode,
- jint action) {
+ jint action, jlong eventTimeNanos) {
VirtualDpad* virtualDpad = reinterpret_cast<VirtualDpad*>(ptr);
- return virtualDpad->writeDpadKeyEvent(androidKeyCode, action);
+ return virtualDpad->writeDpadKeyEvent(androidKeyCode, action,
+ std::chrono::nanoseconds(eventTimeNanos));
}
// Native methods for VirtualKeyboard
static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jlong ptr, jint androidKeyCode,
- jint action) {
+ jint action, jlong eventTimeNanos) {
VirtualKeyboard* virtualKeyboard = reinterpret_cast<VirtualKeyboard*>(ptr);
- return virtualKeyboard->writeKeyEvent(androidKeyCode, action);
+ return virtualKeyboard->writeKeyEvent(androidKeyCode, action,
+ std::chrono::nanoseconds(eventTimeNanos));
}
// Native methods for VirtualTouchscreen
static bool nativeWriteTouchEvent(JNIEnv* env, jobject thiz, jlong ptr, jint pointerId,
jint toolType, jint action, jfloat locationX, jfloat locationY,
- jfloat pressure, jfloat majorAxisSize) {
+ jfloat pressure, jfloat majorAxisSize, jlong eventTimeNanos) {
VirtualTouchscreen* virtualTouchscreen = reinterpret_cast<VirtualTouchscreen*>(ptr);
return virtualTouchscreen->writeTouchEvent(pointerId, toolType, action, locationX, locationY,
- pressure, majorAxisSize);
+ pressure, majorAxisSize,
+ std::chrono::nanoseconds(eventTimeNanos));
}
// Native methods for VirtualMouse
static bool nativeWriteButtonEvent(JNIEnv* env, jobject thiz, jlong ptr, jint buttonCode,
- jint action) {
+ jint action, jlong eventTimeNanos) {
VirtualMouse* virtualMouse = reinterpret_cast<VirtualMouse*>(ptr);
- return virtualMouse->writeButtonEvent(buttonCode, action);
+ return virtualMouse->writeButtonEvent(buttonCode, action,
+ std::chrono::nanoseconds(eventTimeNanos));
}
static bool nativeWriteRelativeEvent(JNIEnv* env, jobject thiz, jlong ptr, jfloat relativeX,
- jfloat relativeY) {
+ jfloat relativeY, jlong eventTimeNanos) {
VirtualMouse* virtualMouse = reinterpret_cast<VirtualMouse*>(ptr);
- return virtualMouse->writeRelativeEvent(relativeX, relativeY);
+ return virtualMouse->writeRelativeEvent(relativeX, relativeY,
+ std::chrono::nanoseconds(eventTimeNanos));
}
static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jlong ptr, jfloat xAxisMovement,
- jfloat yAxisMovement) {
+ jfloat yAxisMovement, jlong eventTimeNanos) {
VirtualMouse* virtualMouse = reinterpret_cast<VirtualMouse*>(ptr);
- return virtualMouse->writeScrollEvent(xAxisMovement, yAxisMovement);
+ return virtualMouse->writeScrollEvent(xAxisMovement, yAxisMovement,
+ std::chrono::nanoseconds(eventTimeNanos));
}
static JNINativeMethod methods[] = {
@@ -283,12 +289,12 @@ static JNINativeMethod methods[] = {
{"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IILjava/lang/String;II)J",
(void*)nativeOpenUinputTouchscreen},
{"nativeCloseUinput", "(J)V", (void*)nativeCloseUinput},
- {"nativeWriteDpadKeyEvent", "(JII)Z", (void*)nativeWriteDpadKeyEvent},
- {"nativeWriteKeyEvent", "(JII)Z", (void*)nativeWriteKeyEvent},
- {"nativeWriteButtonEvent", "(JII)Z", (void*)nativeWriteButtonEvent},
- {"nativeWriteTouchEvent", "(JIIIFFFF)Z", (void*)nativeWriteTouchEvent},
- {"nativeWriteRelativeEvent", "(JFF)Z", (void*)nativeWriteRelativeEvent},
- {"nativeWriteScrollEvent", "(JFF)Z", (void*)nativeWriteScrollEvent},
+ {"nativeWriteDpadKeyEvent", "(JIIJ)Z", (void*)nativeWriteDpadKeyEvent},
+ {"nativeWriteKeyEvent", "(JIIJ)Z", (void*)nativeWriteKeyEvent},
+ {"nativeWriteButtonEvent", "(JIIJ)Z", (void*)nativeWriteButtonEvent},
+ {"nativeWriteTouchEvent", "(JIIIFFFFJ)Z", (void*)nativeWriteTouchEvent},
+ {"nativeWriteRelativeEvent", "(JFFJ)Z", (void*)nativeWriteRelativeEvent},
+ {"nativeWriteScrollEvent", "(JFFJ)Z", (void*)nativeWriteScrollEvent},
};
int register_android_server_companion_virtual_InputController(JNIEnv* env) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 439ad76ced22..cf57b339cc22 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -529,7 +529,7 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
}
base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel(
@@ -1079,7 +1079,7 @@ void NativeInputManager::setPointerDisplayId(int32_t displayId) {
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
}
void NativeInputManager::setPointerSpeed(int32_t speed) {
@@ -1095,7 +1095,7 @@ void NativeInputManager::setPointerSpeed(int32_t speed) {
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_POINTER_SPEED);
+ InputReaderConfiguration::Change::POINTER_SPEED);
}
void NativeInputManager::setPointerAcceleration(float acceleration) {
@@ -1111,7 +1111,7 @@ void NativeInputManager::setPointerAcceleration(float acceleration) {
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_POINTER_SPEED);
+ InputReaderConfiguration::Change::POINTER_SPEED);
}
void NativeInputManager::setTouchpadPointerSpeed(int32_t speed) {
@@ -1127,7 +1127,7 @@ void NativeInputManager::setTouchpadPointerSpeed(int32_t speed) {
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_TOUCHPAD_SETTINGS);
+ InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
}
void NativeInputManager::setTouchpadNaturalScrollingEnabled(bool enabled) {
@@ -1143,7 +1143,7 @@ void NativeInputManager::setTouchpadNaturalScrollingEnabled(bool enabled) {
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_TOUCHPAD_SETTINGS);
+ InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
}
void NativeInputManager::setTouchpadTapToClickEnabled(bool enabled) {
@@ -1159,7 +1159,7 @@ void NativeInputManager::setTouchpadTapToClickEnabled(bool enabled) {
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_TOUCHPAD_SETTINGS);
+ InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
}
void NativeInputManager::setTouchpadRightClickZoneEnabled(bool enabled) {
@@ -1175,7 +1175,7 @@ void NativeInputManager::setTouchpadRightClickZoneEnabled(bool enabled) {
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_TOUCHPAD_SETTINGS);
+ InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
}
void NativeInputManager::setInputDeviceEnabled(uint32_t deviceId, bool enabled) {
@@ -1193,7 +1193,7 @@ void NativeInputManager::setInputDeviceEnabled(uint32_t deviceId, bool enabled)
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_ENABLED_STATE);
+ InputReaderConfiguration::Change::ENABLED_STATE);
}
void NativeInputManager::setShowTouches(bool enabled) {
@@ -1209,7 +1209,7 @@ void NativeInputManager::setShowTouches(bool enabled) {
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+ InputReaderConfiguration::Change::SHOW_TOUCHES);
}
void NativeInputManager::requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) {
@@ -1222,7 +1222,7 @@ void NativeInputManager::setInteractive(bool interactive) {
void NativeInputManager::reloadCalibration() {
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION);
+ InputReaderConfiguration::Change::TOUCH_AFFINE_TRANSFORMATION);
}
void NativeInputManager::setPointerIconType(PointerIconStyle iconId) {
@@ -1516,7 +1516,7 @@ void NativeInputManager::setPointerCapture(const PointerCaptureRequest& request)
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ InputReaderConfiguration::Change::POINTER_CAPTURE);
}
void NativeInputManager::loadPointerIcon(SpriteIcon* icon, int32_t displayId) {
@@ -1626,7 +1626,7 @@ void NativeInputManager::setStylusButtonMotionEventsEnabled(bool enabled) {
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_STYLUS_BUTTON_REPORTING);
+ InputReaderConfiguration::Change::STYLUS_BUTTON_REPORTING);
}
FloatPoint NativeInputManager::getMouseCursorPosition() {
@@ -1649,7 +1649,7 @@ void NativeInputManager::setStylusPointerIconEnabled(bool enabled) {
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
}
// ----------------------------------------------------------------------------
@@ -2300,14 +2300,14 @@ static void nativeReloadKeyboardLayouts(JNIEnv* env, jobject nativeImplObj) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
im->getInputManager()->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS);
+ InputReaderConfiguration::Change::KEYBOARD_LAYOUTS);
}
static void nativeReloadDeviceAliases(JNIEnv* env, jobject nativeImplObj) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
im->getInputManager()->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_DEVICE_ALIAS);
+ InputReaderConfiguration::Change::DEVICE_ALIAS);
}
static void nativeSysfsNodeChanged(JNIEnv* env, jobject nativeImplObj, jstring path) {
@@ -2403,7 +2403,7 @@ static jboolean nativeCanDispatchToDisplay(JNIEnv* env, jobject nativeImplObj, j
static void nativeNotifyPortAssociationsChanged(JNIEnv* env, jobject nativeImplObj) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
im->getInputManager()->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
}
static void nativeSetDisplayEligibilityForPointerCapture(JNIEnv* env, jobject nativeImplObj,
@@ -2416,19 +2416,19 @@ static void nativeSetDisplayEligibilityForPointerCapture(JNIEnv* env, jobject na
static void nativeChangeUniqueIdAssociation(JNIEnv* env, jobject nativeImplObj) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
im->getInputManager()->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ InputReaderConfiguration::Change::DISPLAY_INFO);
}
static void nativeChangeTypeAssociation(JNIEnv* env, jobject nativeImplObj) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
im->getInputManager()->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_DEVICE_TYPE);
+ InputReaderConfiguration::Change::DEVICE_TYPE);
}
static void changeKeyboardLayoutAssociation(JNIEnv* env, jobject nativeImplObj) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
im->getInputManager()->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUT_ASSOCIATION);
+ InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION);
}
static void nativeSetMotionClassifierEnabled(JNIEnv* env, jobject nativeImplObj, jboolean enabled) {
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 981844cf9338..f96ca582c28f 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -153,12 +153,6 @@
<xs:annotation name="nullable"/>
<xs:annotation name="final"/>
</xs:element>
- <!-- The highest (most severe) thermal status at which high-brightness-mode is allowed
- to operate. -->
- <xs:element name="thermalStatusLimit" type="thermalStatus" minOccurs="0" maxOccurs="1">
- <xs:annotation name="nonnull"/>
- <xs:annotation name="final"/>
- </xs:element>
<xs:element name="allowInLowPowerMode" type="xs:boolean" minOccurs="0" maxOccurs="1">
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 8cb483770c85..ad6434e0c545 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -156,7 +156,6 @@ package com.android.server.display.config {
method @NonNull public final java.math.BigDecimal getMinimumLux_all();
method @Nullable public final com.android.server.display.config.RefreshRateRange getRefreshRate_all();
method @Nullable public final com.android.server.display.config.SdrHdrRatioMap getSdrHdrRatioMap_all();
- method @NonNull public final com.android.server.display.config.ThermalStatus getThermalStatusLimit_all();
method public com.android.server.display.config.HbmTiming getTiming_all();
method @NonNull public final java.math.BigDecimal getTransitionPoint_all();
method public final void setAllowInLowPowerMode_all(@NonNull boolean);
@@ -165,7 +164,6 @@ package com.android.server.display.config {
method public final void setMinimumLux_all(@NonNull java.math.BigDecimal);
method public final void setRefreshRate_all(@Nullable com.android.server.display.config.RefreshRateRange);
method public final void setSdrHdrRatioMap_all(@Nullable com.android.server.display.config.SdrHdrRatioMap);
- method public final void setThermalStatusLimit_all(@NonNull com.android.server.display.config.ThermalStatus);
method public void setTiming_all(com.android.server.display.config.HbmTiming);
method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal);
}
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index f39de43b5076..0271727249b1 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -19,7 +19,6 @@ package com.android.server.credentials;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
-import android.credentials.CredentialOption;
import android.credentials.CredentialProviderInfo;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
@@ -36,7 +35,6 @@ import com.android.server.credentials.metrics.ProviderStatusForMetrics;
import java.util.ArrayList;
import java.util.Set;
-import java.util.stream.Collectors;
/**
* Central session for a single getCredentials request. This class listens to the
@@ -56,11 +54,7 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest,
super(context, sessionCallback, lock, userId, callingUid, request, callback,
RequestInfo.TYPE_GET, callingAppInfo, enabledProviders, cancellationSignal,
startedTimestamp);
- int numTypes = (request.getCredentialOptions().stream()
- .map(CredentialOption::getType).collect(
- Collectors.toSet())).size(); // Dedupe type strings
- mRequestSessionMetric.collectGetFlowInitialMetricInfo(numTypes,
- /*origin=*/request.getOrigin() != null);
+ mRequestSessionMetric.collectGetFlowInitialMetricInfo(request);
}
/**
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index e25614846896..50e5163cea55 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -43,11 +43,15 @@ public class MetricUtilities {
public static final String USER_CANCELED_SUBSTRING = "TYPE_USER_CANCELED";
public static final int DEFAULT_INT_32 = -1;
+ public static final String DEFAULT_STRING = "";
public static final int[] DEFAULT_REPEATED_INT_32 = new int[0];
+ public static final String[] DEFAULT_REPEATED_STR = new String[0];
// Used for single count metric emits, such as singular amounts of various types
public static final int UNIT = 1;
// Used for zero count metric emits, such as zero amounts of various types
public static final int ZERO = 0;
+ // The number of characters at the end of the string to use as a key
+ public static final int DELTA_CUT = 20;
/**
* This retrieves the uid of any package name, given a context and a component name for the
@@ -85,6 +89,18 @@ public class MetricUtilities {
}
/**
+ * Given the current design, we can designate how the strings in the backend should appear.
+ * This helper method lets us cut strings for our class types.
+ *
+ * @param classtype the classtype string we want to cut to generate a key
+ * @param deltaFromEnd the starting point from the end of the string we wish to begin at
+ * @return the cut up string key we want to use for metric logs
+ */
+ public static String generateMetricKey(String classtype, int deltaFromEnd) {
+ return classtype.substring(classtype.length() - deltaFromEnd);
+ }
+
+ /**
* A logging utility used primarily for the final phase of the current metric setup.
*
* @param finalPhaseMetric the coalesced data of the chosen provider
@@ -143,7 +159,12 @@ public class MetricUtilities {
finalPhaseMetric.getAuthenticationEntryCount(),
/* clicked_entries */ browsedClickedEntries,
/* provider_of_clicked_entry */ browsedProviderUid,
- /* api_status */ apiStatus
+ /* api_status */ apiStatus,
+ DEFAULT_REPEATED_INT_32,
+ DEFAULT_REPEATED_INT_32,
+ DEFAULT_REPEATED_STR,
+ DEFAULT_REPEATED_INT_32,
+ DEFAULT_STRING
);
} catch (Exception e) {
Log.w(TAG, "Unexpected error during metric logging: " + e);
@@ -151,13 +172,17 @@ public class MetricUtilities {
}
/**
- * A logging utility used primarily for the candidate phase of the current metric setup.
+ * A logging utility used primarily for the candidate phase of the current metric setup. This
+ * will primarily focus on track 2, where the session id is associated with known providers,
+ * but NOT the calling app.
*
* @param providers a map with known providers and their held metric objects
* @param emitSequenceId an emitted sequence id for the current session
+ * @param initialPhaseMetric contains initial phase data to avoid repetition for candidate
+ * phase, track 2, logging
*/
public static void logApiCalledCandidatePhase(Map<String, ProviderSession> providers,
- int emitSequenceId) {
+ int emitSequenceId, InitialPhaseMetric initialPhaseMetric) {
try {
if (!LOG_FLAG) {
return;
@@ -177,6 +202,7 @@ public class MetricUtilities {
int[] candidateActionEntryCountList = new int[providerSize];
int[] candidateAuthEntryCountList = new int[providerSize];
int[] candidateRemoteEntryCountList = new int[providerSize];
+ String[] frameworkExceptionList = new String[providerSize];
int index = 0;
for (var session : providerSessions) {
CandidatePhaseMetric metric = session.mProviderSessionMetric
@@ -202,6 +228,7 @@ public class MetricUtilities {
candidateActionEntryCountList[index] = metric.getActionEntryCount();
candidateAuthEntryCountList[index] = metric.getAuthenticationEntryCount();
candidateRemoteEntryCountList[index] = metric.getRemoteEntryCount();
+ frameworkExceptionList[index] = metric.getFrameworkException();
index++;
}
FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_CANDIDATE_PHASE_REPORTED,
@@ -222,7 +249,16 @@ public class MetricUtilities {
/* candidate_provider_credential_entry_type_count */
candidateCredentialTypeCountList,
/* candidate_provider_remote_entry_count */ candidateRemoteEntryCountList,
- /* candidate_provider_authentication_entry_count */ candidateAuthEntryCountList
+ /* candidate_provider_authentication_entry_count */
+ candidateAuthEntryCountList,
+ /* framework_exception_per_provider */
+ frameworkExceptionList,
+ /* origin_specified originSpecified */
+ initialPhaseMetric.isOriginSpecified(),
+ /* request_unique_classtypes */
+ initialPhaseMetric.getUniqueRequestStrings(),
+ /* per_classtype_counts */
+ initialPhaseMetric.getUniqueRequestCounts()
);
} catch (Exception e) {
Log.w(TAG, "Unexpected error during metric logging: " + e);
@@ -285,10 +321,13 @@ public class MetricUtilities {
/* initial_timestamp_reference_nanoseconds */
initialPhaseMetric.getCredentialServiceStartedTimeNanoseconds(),
/* count_credential_request_classtypes */
- initialPhaseMetric.getCountRequestClassType()
- // TODO(b/271135048) - add total count of request options
- // TODO(b/271135048) - Uncomment once built past PWG review -
- // initialPhaseMetric.isOriginSpecified()
+ initialPhaseMetric.getCountRequestClassType(),
+ /* request_unique_classtypes */
+ initialPhaseMetric.getUniqueRequestStrings(),
+ /* per_classtype_counts */
+ initialPhaseMetric.getUniqueRequestCounts(),
+ /* origin_specified */
+ initialPhaseMetric.isOriginSpecified()
);
} catch (Exception e) {
Log.w(TAG, "Unexpected error during metric logging: " + e);
diff --git a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
index 1c3d213c8072..441c87b1569a 100644
--- a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
@@ -59,8 +59,7 @@ public class PrepareGetRequestSession extends GetRequestSession {
int numTypes = (request.getCredentialOptions().stream()
.map(CredentialOption::getType).collect(
Collectors.toSet())).size(); // Dedupe type strings
- mRequestSessionMetric.collectGetFlowInitialMetricInfo(numTypes,
- /*origin=*/request.getOrigin() != null);
+ mRequestSessionMetric.collectGetFlowInitialMetricInfo(request);
mPrepareGetCredentialCallback = prepareGetCredentialCallback;
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
index 9ec0ecd93b3c..8af6b56f881d 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
@@ -91,6 +91,8 @@ public final class ProviderClearSession extends ProviderSession<ClearCredentialS
public void onProviderResponseFailure(int errorCode, Exception exception) {
if (exception instanceof ClearCredentialStateException) {
mProviderException = (ClearCredentialStateException) exception;
+ // TODO(b/271135048) : Decide on exception type length
+ mProviderSessionMetric.collectCandidateFrameworkException(mProviderException.getType());
}
mProviderSessionMetric.collectCandidateExceptionStatus(/*hasException=*/true);
updateStatusAndInvokeCallback(toStatus(errorCode),
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index 09433dbb0c52..520b937d24c5 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -155,6 +155,8 @@ public final class ProviderCreateSession extends ProviderSession<
if (exception instanceof CreateCredentialException) {
// Store query phase exception for aggregation with final response
mProviderException = (CreateCredentialException) exception;
+ // TODO(b/271135048) : Decide on exception type length
+ mProviderSessionMetric.collectCandidateFrameworkException(mProviderException.getType());
}
mProviderSessionMetric.collectCandidateExceptionStatus(/*hasException=*/true);
updateStatusAndInvokeCallback(toStatus(errorCode),
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 0c2b5633d501..a62d9e805a93 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -217,6 +217,8 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
public void onProviderResponseFailure(int errorCode, Exception exception) {
if (exception instanceof GetCredentialException) {
mProviderException = (GetCredentialException) exception;
+ // TODO(b/271135048) : Decide on exception type length
+ mProviderSessionMetric.collectCandidateFrameworkException(mProviderException.getType());
}
mProviderSessionMetric.collectCandidateExceptionStatus(/*hasException=*/true);
updateStatusAndInvokeCallback(toStatus(errorCode),
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ApiName.java b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java
index ce84d9af8c14..b99f28d07f75 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ApiName.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java
@@ -57,7 +57,7 @@ CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_IS_ENABLED_CREDENT
);
ApiName(int innerMetricCode) {
- this.mInnerMetricCode = innerMetricCode;
+ mInnerMetricCode = innerMetricCode;
}
/**
@@ -66,7 +66,7 @@ CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_IS_ENABLED_CREDENT
* @return a code corresponding to the west world metric name
*/
public int getMetricCode() {
- return this.mInnerMetricCode;
+ return mInnerMetricCode;
}
/**
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java b/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java
index 4097765b8736..ece729fe5b32 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ApiStatus.java
@@ -32,7 +32,7 @@ public enum ApiStatus {
private final int mInnerMetricCode;
ApiStatus(int innerMetricCode) {
- this.mInnerMetricCode = innerMetricCode;
+ mInnerMetricCode = innerMetricCode;
}
/**
@@ -41,6 +41,6 @@ public enum ApiStatus {
* @return a code corresponding to the west world metric name
*/
public int getMetricCode() {
- return this.mInnerMetricCode;
+ return mInnerMetricCode;
}
}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
index 10d4f9c7590e..721d3d782653 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/CandidatePhaseMetric.java
@@ -73,6 +73,8 @@ public class CandidatePhaseMetric {
private int mAuthenticationEntryCount = -1;
// Gathered to pass on to chosen provider when required
private final IntArray mAvailableEntries = new IntArray();
+ // The *framework only* exception held by this provider, empty string by default
+ private String mFrameworkException = "";
public CandidatePhaseMetric() {
}
@@ -82,27 +84,27 @@ public class CandidatePhaseMetric {
/* -- Timestamps -- */
public void setServiceBeganTimeNanoseconds(long serviceBeganTimeNanoseconds) {
- this.mServiceBeganTimeNanoseconds = serviceBeganTimeNanoseconds;
+ mServiceBeganTimeNanoseconds = serviceBeganTimeNanoseconds;
}
public void setStartQueryTimeNanoseconds(long startQueryTimeNanoseconds) {
- this.mStartQueryTimeNanoseconds = startQueryTimeNanoseconds;
+ mStartQueryTimeNanoseconds = startQueryTimeNanoseconds;
}
public void setQueryFinishTimeNanoseconds(long queryFinishTimeNanoseconds) {
- this.mQueryFinishTimeNanoseconds = queryFinishTimeNanoseconds;
+ mQueryFinishTimeNanoseconds = queryFinishTimeNanoseconds;
}
public long getServiceBeganTimeNanoseconds() {
- return this.mServiceBeganTimeNanoseconds;
+ return mServiceBeganTimeNanoseconds;
}
public long getStartQueryTimeNanoseconds() {
- return this.mStartQueryTimeNanoseconds;
+ return mStartQueryTimeNanoseconds;
}
public long getQueryFinishTimeNanoseconds() {
- return this.mQueryFinishTimeNanoseconds;
+ return mQueryFinishTimeNanoseconds;
}
/* -- Actual time delta latencies (for local utility) -- */
@@ -111,8 +113,8 @@ public class CandidatePhaseMetric {
* Returns the latency in microseconds for the query phase.
*/
public int getQueryLatencyMicroseconds() {
- return (int) ((this.getQueryFinishTimeNanoseconds()
- - this.getStartQueryTimeNanoseconds()) / 1000);
+ return (int) ((getQueryFinishTimeNanoseconds()
+ - getStartQueryTimeNanoseconds()) / 1000);
}
/* --- Time Stamp Conversion to Microseconds from Reference --- */
@@ -126,32 +128,32 @@ public class CandidatePhaseMetric {
* @return the microsecond integer timestamp from service start to query began
*/
public int getTimestampFromReferenceStartMicroseconds(long specificTimestamp) {
- if (specificTimestamp < this.mServiceBeganTimeNanoseconds) {
+ if (specificTimestamp < mServiceBeganTimeNanoseconds) {
Log.i(TAG, "The timestamp is before service started, falling back to default int");
return MetricUtilities.DEFAULT_INT_32;
}
return (int) ((specificTimestamp
- - this.mServiceBeganTimeNanoseconds) / 1000);
+ - mServiceBeganTimeNanoseconds) / 1000);
}
/* ------------- Provider Query Status ------------ */
public void setProviderQueryStatus(int providerQueryStatus) {
- this.mProviderQueryStatus = providerQueryStatus;
+ mProviderQueryStatus = providerQueryStatus;
}
public int getProviderQueryStatus() {
- return this.mProviderQueryStatus;
+ return mProviderQueryStatus;
}
/* -------------- Candidate Uid ---------------- */
public void setCandidateUid(int candidateUid) {
- this.mCandidateUid = candidateUid;
+ mCandidateUid = candidateUid;
}
public int getCandidateUid() {
- return this.mCandidateUid;
+ return mCandidateUid;
}
/* -------------- Session Id ---------------- */
@@ -254,7 +256,7 @@ public class CandidatePhaseMetric {
* collector
*/
public void addEntry(EntryEnum e) {
- this.mAvailableEntries.add(e.getMetricCode());
+ mAvailableEntries.add(e.getMetricCode());
}
/**
@@ -267,4 +269,14 @@ public class CandidatePhaseMetric {
public List<Integer> getAvailableEntries() {
return Arrays.stream(mAvailableEntries.toArray()).boxed().collect(Collectors.toList());
}
+
+ /* ------ Framework Exception for this Candidate ------ */
+
+ public void setFrameworkException(String frameworkException) {
+ mFrameworkException = frameworkException;
+ }
+
+ public String getFrameworkException() {
+ return mFrameworkException;
+ }
}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java
index 2eef19732723..c80cc24fa455 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderFinalPhaseMetric.java
@@ -138,8 +138,8 @@ public class ChosenProviderFinalPhaseMetric {
}
public int getUiPhaseLatencyMicroseconds() {
- return (int) ((this.mUiCallEndTimeNanoseconds
- - this.mUiCallStartTimeNanoseconds) / 1000);
+ return (int) ((mUiCallEndTimeNanoseconds
+ - mUiCallStartTimeNanoseconds) / 1000);
}
/**
@@ -147,8 +147,8 @@ public class ChosenProviderFinalPhaseMetric {
* start time to be provided, such as from {@link CandidatePhaseMetric}.
*/
public int getEntireProviderLatencyMicroseconds() {
- return (int) ((this.mFinalFinishTimeNanoseconds
- - this.mQueryStartTimeNanoseconds) / 1000);
+ return (int) ((mFinalFinishTimeNanoseconds
+ - mQueryStartTimeNanoseconds) / 1000);
}
/**
@@ -156,8 +156,8 @@ public class ChosenProviderFinalPhaseMetric {
* start time to be provided, such as from {@link InitialPhaseMetric}.
*/
public int getEntireLatencyMicroseconds() {
- return (int) ((this.mFinalFinishTimeNanoseconds
- - this.mServiceBeganTimeNanoseconds) / 1000);
+ return (int) ((mFinalFinishTimeNanoseconds
+ - mServiceBeganTimeNanoseconds) / 1000);
}
/* ----- Timestamps for Latency ----- */
@@ -183,11 +183,11 @@ public class ChosenProviderFinalPhaseMetric {
}
public void setUiCallStartTimeNanoseconds(long uiCallStartTimeNanoseconds) {
- this.mUiCallStartTimeNanoseconds = uiCallStartTimeNanoseconds;
+ mUiCallStartTimeNanoseconds = uiCallStartTimeNanoseconds;
}
public void setUiCallEndTimeNanoseconds(long uiCallEndTimeNanoseconds) {
- this.mUiCallEndTimeNanoseconds = uiCallEndTimeNanoseconds;
+ mUiCallEndTimeNanoseconds = uiCallEndTimeNanoseconds;
}
public void setFinalFinishTimeNanoseconds(long finalFinishTimeNanoseconds) {
@@ -229,12 +229,12 @@ public class ChosenProviderFinalPhaseMetric {
* @return the microsecond integer timestamp from service start to query began
*/
public int getTimestampFromReferenceStartMicroseconds(long specificTimestamp) {
- if (specificTimestamp < this.mServiceBeganTimeNanoseconds) {
+ if (specificTimestamp < mServiceBeganTimeNanoseconds) {
Log.i(TAG, "The timestamp is before service started, falling back to default int");
return MetricUtilities.DEFAULT_INT_32;
}
return (int) ((specificTimestamp
- - this.mServiceBeganTimeNanoseconds) / 1000);
+ - mServiceBeganTimeNanoseconds) / 1000);
}
/* ----------- Provider Status -------------- */
@@ -334,7 +334,7 @@ public class ChosenProviderFinalPhaseMetric {
* chosen phase in a semantically correct way.
*/
public void setAvailableEntries(List<Integer> entries) {
- this.mAvailableEntries = new ArrayList<>(entries); // no alias copy
+ mAvailableEntries = new ArrayList<>(entries); // no alias copy
}
/**
@@ -345,7 +345,7 @@ public class ChosenProviderFinalPhaseMetric {
* candidate phase.
*/
public List<Integer> getAvailableEntries() {
- return new ArrayList<>(this.mAvailableEntries); // no alias copy
+ return new ArrayList<>(mAvailableEntries); // no alias copy
}
/* -------------- Has Exception ---------------- */
diff --git a/services/credentials/java/com/android/server/credentials/metrics/EntryEnum.java b/services/credentials/java/com/android/server/credentials/metrics/EntryEnum.java
index 80f9fdc85a7a..b9125ddf1145 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/EntryEnum.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/EntryEnum.java
@@ -56,7 +56,7 @@ public enum EntryEnum {
);
EntryEnum(int innerMetricCode) {
- this.mInnerMetricCode = innerMetricCode;
+ mInnerMetricCode = innerMetricCode;
}
/**
@@ -65,7 +65,7 @@ public enum EntryEnum {
* @return a code corresponding to the west world metric name
*/
public int getMetricCode() {
- return this.mInnerMetricCode;
+ return mInnerMetricCode;
}
/**
diff --git a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
index 0210b14943db..0ecd9cc79e48 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
@@ -16,6 +16,11 @@
package com.android.server.credentials.metrics;
+import android.util.Log;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
/**
* This handles metrics collected prior to any remote calls to providers.
* Some types are redundant across these metric collectors, but that has debug use-cases as
@@ -32,7 +37,6 @@ public class InitialPhaseMetric {
private int mCallerUid = -1;
// The session id to unite multiple atom emits, default to -1
private int mSessionId = -1;
- private int mCountRequestClassType = -1;
// Raw timestamps in nanoseconds, *the only* one logged as such (i.e. 64 bits) since it is a
// reference point.
@@ -46,6 +50,9 @@ public class InitialPhaseMetric {
// TODO(b/271135048) - Emit once metrics approved
private boolean mOriginSpecified = false;
+ // Stores the deduped request information, particularly {"req":5}.
+ private Map<String, Integer> mRequestCounts = new LinkedHashMap<>();
+
public InitialPhaseMetric() {
}
@@ -55,8 +62,8 @@ public class InitialPhaseMetric {
/* -- Direct Latency Utility -- */
public int getServiceStartToQueryLatencyMicroseconds() {
- return (int) ((this.mCredentialServiceStartedTimeNanoseconds
- - this.mCredentialServiceBeginQueryTimeNanoseconds) / 1000);
+ return (int) ((mCredentialServiceStartedTimeNanoseconds
+ - mCredentialServiceBeginQueryTimeNanoseconds) / 1000);
}
/* -- Timestamps -- */
@@ -64,7 +71,7 @@ public class InitialPhaseMetric {
public void setCredentialServiceStartedTimeNanoseconds(
long credentialServiceStartedTimeNanoseconds
) {
- this.mCredentialServiceStartedTimeNanoseconds = credentialServiceStartedTimeNanoseconds;
+ mCredentialServiceStartedTimeNanoseconds = credentialServiceStartedTimeNanoseconds;
}
public void setCredentialServiceBeginQueryTimeNanoseconds(
@@ -112,14 +119,12 @@ public class InitialPhaseMetric {
/* ------ Count Request Class Types ------ */
- public void setCountRequestClassType(int countRequestClassType) {
- mCountRequestClassType = countRequestClassType;
- }
-
public int getCountRequestClassType() {
- return mCountRequestClassType;
+ return mRequestCounts.size();
}
+ /* ------ Origin Specified ------ */
+
public void setOriginSpecified(boolean originSpecified) {
mOriginSpecified = originSpecified;
}
@@ -127,4 +132,34 @@ public class InitialPhaseMetric {
public boolean isOriginSpecified() {
return mOriginSpecified;
}
+
+ /* ------ Unique Request Counts Map Information ------ */
+
+ public void setRequestCounts(Map<String, Integer> requestCounts) {
+ mRequestCounts = requestCounts;
+ }
+
+ /**
+ * Reruns the unique, deduped, request classtypes for logging.
+ * @return a string array for deduped classtypes
+ */
+ public String[] getUniqueRequestStrings() {
+ if (mRequestCounts.isEmpty()) {
+ Log.w(TAG, "There are no unique string request types collected");
+ }
+ String[] result = new String[mRequestCounts.keySet().size()];
+ mRequestCounts.keySet().toArray(result);
+ return result;
+ }
+
+ /**
+ * Reruns the unique, deduped, request classtype counts for logging.
+ * @return a string array for deduped classtype counts
+ */
+ public int[] getUniqueRequestCounts() {
+ if (mRequestCounts.isEmpty()) {
+ Log.w(TAG, "There are no unique string request type counts collected");
+ }
+ return mRequestCounts.values().stream().mapToInt(Integer::intValue).toArray();
+ }
}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java
index 76fd4786f9ee..9a88255ce973 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ProviderSessionMetric.java
@@ -61,6 +61,18 @@ public class ProviderSessionMetric {
}
/**
+ * Collects the framework only exception encountered in a candidate flow.
+ * @param exceptionType the string, cut to desired length, of the exception type
+ */
+ public void collectCandidateFrameworkException(String exceptionType) {
+ try {
+ mCandidatePhasePerProviderMetric.setFrameworkException(exceptionType);
+ } catch (Exception e) {
+ Log.w(TAG, "Unexpected error during metric logging: " + e);
+ }
+ }
+
+ /**
* Used to collect metrics at the update stage when a candidate provider gives back an update.
*
* @param isFailureStatus indicates the candidate provider sent back a terminated response
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ProviderStatusForMetrics.java b/services/credentials/java/com/android/server/credentials/metrics/ProviderStatusForMetrics.java
index a12a6942a5c2..b1e6a4c78fb0 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ProviderStatusForMetrics.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ProviderStatusForMetrics.java
@@ -38,7 +38,7 @@ public enum ProviderStatusForMetrics {
private final int mInnerMetricCode;
ProviderStatusForMetrics(int innerMetricCode) {
- this.mInnerMetricCode = innerMetricCode;
+ mInnerMetricCode = innerMetricCode;
}
/**
@@ -47,6 +47,6 @@ public enum ProviderStatusForMetrics {
* @return a code corresponding to the west world metric name
*/
public int getMetricCode() {
- return this.mInnerMetricCode;
+ return mInnerMetricCode;
}
}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
index 10bf56c853f5..547c09a625f6 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
@@ -16,10 +16,12 @@
package com.android.server.credentials.metrics;
+import static com.android.server.credentials.MetricUtilities.DELTA_CUT;
+import static com.android.server.credentials.MetricUtilities.generateMetricKey;
import static com.android.server.credentials.MetricUtilities.logApiCalledCandidatePhase;
import static com.android.server.credentials.MetricUtilities.logApiCalledFinalPhase;
-import android.annotation.NonNull;
+import android.credentials.GetCredentialRequest;
import android.credentials.ui.UserSelectionDialogResult;
import android.os.IBinder;
import android.util.Log;
@@ -27,6 +29,7 @@ import android.util.Log;
import com.android.server.credentials.ProviderSession;
import java.util.ArrayList;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -48,7 +51,6 @@ public class RequestSessionMetric {
protected final ChosenProviderFinalPhaseMetric
mChosenProviderFinalPhaseMetric = new ChosenProviderFinalPhaseMetric();
// TODO(b/271135048) - Replace this with a new atom per each browsing emit (V4)
- @NonNull
protected List<CandidateBrowsingPhaseMetric> mCandidateBrowsingPhaseMetric = new ArrayList<>();
public RequestSessionMetric() {
@@ -161,16 +163,32 @@ public class RequestSessionMetric {
}
}
+ // Used by get flows to generate the unique request count maps
+ private Map<String, Integer> getRequestCountMap(GetCredentialRequest request) {
+ Map<String, Integer> uniqueRequestCounts = new LinkedHashMap<>();
+ try {
+ request.getCredentialOptions().forEach(option -> {
+ String optionKey = generateMetricKey(option.getType(), DELTA_CUT);
+ if (!uniqueRequestCounts.containsKey(optionKey)) {
+ uniqueRequestCounts.put(optionKey, 0);
+ }
+ uniqueRequestCounts.put(optionKey, uniqueRequestCounts.get(optionKey) + 1);
+ });
+ } catch (Exception e) {
+ Log.w(TAG, "Unexpected error during get request metric logging: " + e);
+ }
+ return uniqueRequestCounts;
+ }
+
/**
* Collects initializations for Get flow metrics.
*
- * @param requestClassTypeCount the number of class types in the request
- * @param origin indicates if an origin was passed in or not
+ * @param request the get credential request containing information to parse for metrics
*/
- public void collectGetFlowInitialMetricInfo(int requestClassTypeCount, boolean origin) {
+ public void collectGetFlowInitialMetricInfo(GetCredentialRequest request) {
try {
- mInitialPhaseMetric.setCountRequestClassType(requestClassTypeCount);
- mInitialPhaseMetric.setOriginSpecified(origin);
+ mInitialPhaseMetric.setOriginSpecified(request.getOrigin() != null);
+ mInitialPhaseMetric.setRequestCounts(getRequestCountMap(request));
} catch (Exception e) {
Log.w(TAG, "Unexpected error during metric logging: " + e);
}
@@ -306,7 +324,7 @@ public class RequestSessionMetric {
*/
public void logCandidatePhaseMetrics(Map<String, ProviderSession> providers) {
try {
- logApiCalledCandidatePhase(providers, ++mSequenceCounter);
+ logApiCalledCandidatePhase(providers, ++mSequenceCounter, mInitialPhaseMetric);
} catch (Exception e) {
Log.w(TAG, "Unexpected error during metric logging: " + e);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7e5d5aae06e4..f6bc93ab2491 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -160,6 +160,7 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED;
import static android.app.admin.DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED;
import static android.app.admin.DevicePolicyManager.PERSONAL_APPS_SUSPENDED_EXPLICITLY;
@@ -533,7 +534,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -1186,9 +1186,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Resume logging if all remaining users are affiliated.
maybeResumeDeviceWideLoggingLocked();
}
- if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
- mDevicePolicyEngine.handleUserRemoved(userHandle);
- }
+ }
+ if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+ mDevicePolicyEngine.handleUserRemoved(userHandle);
}
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STARTED, userHandle);
@@ -4157,8 +4157,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_ACTIVE_ADMIN);
enforceUserUnlocked(userHandle);
+ ActiveAdmin admin;
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
+ admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (admin == null) {
return;
}
@@ -4169,14 +4170,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ adminReceiver);
return;
}
-
mInjector.binderWithCleanCallingIdentity(() ->
removeActiveAdminLocked(adminReceiver, userHandle));
- if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
- mDevicePolicyEngine.removePoliciesForAdmin(
- EnforcingAdmin.createEnterpriseEnforcingAdmin(
- adminReceiver, userHandle, admin));
- }
+ }
+ if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+ mDevicePolicyEngine.removePoliciesForAdmin(
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ adminReceiver, userHandle, admin));
}
}
@@ -5865,8 +5865,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// would allow bypassing of the maximum time to lock.
mInjector.settingsGlobalPutInt(Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
}
- getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin(
- UserHandle.USER_SYSTEM, timeMs);
+ getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin(parentId, timeMs);
});
}
@@ -9199,9 +9198,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_CAMERA,
caller.getPackageName(),
getProfileParentUserIfRequested(userId, parent));
-
- setBackwardCompatibleUserRestriction(
- caller, enforcingAdmin, UserManager.DISALLOW_CAMERA, disabled, parent);
+ try {
+ setBackwardCompatibleUserRestriction(
+ caller, enforcingAdmin, UserManager.DISALLOW_CAMERA, disabled, parent);
+ } catch (IllegalStateException e) {
+ throw new IllegalStateException(
+ "Please use addUserRestriction or addUserRestrictionGlobally using the key"
+ + " UserManager.DISALLOW_CAMERA to disable the camera locally or"
+ + " globally, respectively");
+ }
} else {
Objects.requireNonNull(who, "ComponentName is null");
if (parent) {
@@ -13804,8 +13809,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
admin,
new BooleanPolicyValue(hidden),
userId);
- Boolean resolvedPolicy = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.APPLICATION_HIDDEN(packageName), userId);
result = mInjector.binderWithCleanCallingIdentity(() -> {
try {
// This is a best effort to continue returning the same value that was
@@ -15467,11 +15470,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
int userId = caller.getUserId();
synchronized (getLockObject()) {
- Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId),
- "Admin " + who
- + " is neither the device owner or affiliated user's profile owner.");
- if (isManagedProfile(userId)) {
- throw new SecurityException("Managed profile cannot disable status bar");
+ if (!isPermissionCheckFlagEnabled()) {
+ Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId),
+ "Admin " + who + " is neither the device owner or affiliated "
+ + "user's profile owner.");
+ if (isManagedProfile(userId)) {
+ throw new SecurityException("Managed profile cannot disable status bar");
+ }
}
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_STATUS_BAR_DISABLED);
@@ -15524,16 +15529,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean isStatusBarDisabled(String callerPackage) {
final CallerIdentity caller = getCallerIdentity(callerPackage);
- Preconditions.checkCallAuthorization(
- isProfileOwner(caller) || isDefaultDeviceOwner(caller));
+ if (isPermissionCheckFlagEnabled()) {
+ enforceCanQuery(
+ MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(), caller.getUserId());
+ } else {
+ Preconditions.checkCallAuthorization(
+ isProfileOwner(caller) || isDefaultDeviceOwner(caller));
+ }
int userId = caller.getUserId();
synchronized (getLockObject()) {
- Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId),
- "Admin " + callerPackage
- + " is neither the device owner or affiliated user's profile owner.");
- if (isManagedProfile(userId)) {
- throw new SecurityException("Managed profile cannot disable status bar");
+ if (!isPermissionCheckFlagEnabled()) {
+ Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId),
+ "Admin " + callerPackage
+ + " is neither the device owner or affiliated user's profile owner.");
+ if (isManagedProfile(userId)) {
+ throw new SecurityException("Managed profile cannot disable status bar");
+ }
}
DevicePolicyData policy = getUserData(userId);
return policy.mStatusBarDisabled;
@@ -16549,11 +16561,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
hasCallingOrSelfPermission(permission.NOTIFY_PENDING_SYSTEM_UPDATE),
"Only the system update service can broadcast update information");
- if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
- Slogf.w(LOG_TAG, "Only the system update service in the system user can broadcast "
- + "update information.");
- return;
- }
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ if (!mUserManager.getUserInfo(UserHandle.getCallingUserId()).isMain()) {
+ Slogf.w(LOG_TAG, "Only the system update service in the main user can broadcast "
+ + "update information.");
+ return;
+ }
+ });
if (!mOwners.saveSystemUpdateInfo(info)) {
// Pending system update hasn't changed, don't send duplicate notification.
@@ -16661,8 +16675,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
enforcePermissionGrantStateOnFinancedDevice(packageName, permission);
}
}
+ EnforcingAdmin enforcingAdmin;
if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
admin,
MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
callerPackage,
@@ -16686,17 +16701,6 @@ 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.
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.PERMISSION_GRANT(packageName, permission),
- enforcingAdmin,
- new IntegerPolicyValue(grantState),
- caller.getUserId());
- // TODO: update javadoc to reflect that callback no longer return success/failure
- callback.sendResult(Bundle.EMPTY);
} else {
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
@@ -16704,51 +16708,81 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|| (caller.hasPackage() && isCallerDelegate(caller,
DELEGATION_PERMISSION_GRANT)));
synchronized (getLockObject()) {
- long ident = mInjector.binderClearCallingIdentity();
- 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())
- < android.os.Build.VERSION_CODES.M) {
+ long ident = mInjector.binderClearCallingIdentity();
+ 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())
+ < android.os.Build.VERSION_CODES.M) {
+ callback.sendResult(null);
+ return;
+ }
+ }
+ if (!isRuntimePermission(permission)) {
callback.sendResult(null);
return;
}
- }
- if (!isRuntimePermission(permission)) {
+ } catch (SecurityException e) {
+ Slogf.e(LOG_TAG, "Could not set permission grant state", e);
callback.sendResult(null);
- return;
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
}
- if (grantState == PERMISSION_GRANT_STATE_GRANTED
- || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
- || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) {
- AdminPermissionControlParams permissionParams =
- new AdminPermissionControlParams(packageName, permission,
- grantState,
- canAdminGrantSensorsPermissions());
- mInjector.getPermissionControllerManager(caller.getUserHandle())
- .setRuntimePermissionGrantStateByDeviceAdmin(
- caller.getPackageName(),
- permissionParams, mContext.getMainExecutor(),
- (permissionWasSet) -> {
- if (isPostQAdmin && !permissionWasSet) {
- callback.sendResult(null);
- return;
- }
-
- DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums
- .SET_PERMISSION_GRANT_STATE)
- .setAdmin(caller.getPackageName())
- .setStrings(permission)
- .setInt(grantState)
- .setBoolean(
- /* isDelegate */ isCallerDelegate(caller))
- .write();
-
- callback.sendResult(Bundle.EMPTY);
- });
+ }
+ }
+ // 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) {
+ AdminPermissionControlParams permissionParams =
+ new AdminPermissionControlParams(packageName, permission,
+ grantState,
+ canAdminGrantSensorsPermissions());
+ mInjector.getPermissionControllerManager(caller.getUserHandle())
+ .setRuntimePermissionGrantStateByDeviceAdmin(
+ caller.getPackageName(),
+ permissionParams, mContext.getMainExecutor(),
+ (permissionWasSet) -> {
+ if (isPostQAdmin && !permissionWasSet) {
+ callback.sendResult(null);
+ return;
+ }
+ callback.sendResult(Bundle.EMPTY);
+ });
}
} catch (SecurityException e) {
Slogf.e(LOG_TAG, "Could not set permission grant state", e);
@@ -16759,6 +16793,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
}
+ DevicePolicyEventLogger.createEvent(DevicePolicyEnums.SET_PERMISSION_GRANT_STATE)
+ .setAdmin(caller.getPackageName())
+ .setStrings(permission)
+ .setInt(grantState)
+ .setBoolean(/* isDelegate */ isCallerDelegate(caller))
+ .write();
}
private static final List<String> SENSOR_PERMISSIONS = new ArrayList<>();
@@ -16822,10 +16862,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (isFinancedDeviceOwner(caller)) {
enforcePermissionGrantStateOnFinancedDevice(packageName, permission);
}
- return mInjector.binderWithCleanCallingIdentity(() -> {
- return getPermissionGrantStateForUser(
- packageName, permission, caller, caller.getUserId());
- });
+ return mInjector.binderWithCleanCallingIdentity(() -> getPermissionGrantStateForUser(
+ packageName, permission, caller, caller.getUserId()));
}
}
@@ -18952,7 +18990,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
admin,
MANAGE_DEVICE_POLICY_RESET_PASSWORD,
caller.getPackageName(),
- UserHandle.USER_ALL);
+ userId);
Long currentTokenHandle = mDevicePolicyEngine.getLocalPolicySetByAdmin(
PolicyDefinition.RESET_PASSWORD_TOKEN,
enforcingAdmin,
@@ -19016,7 +19054,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
admin,
MANAGE_DEVICE_POLICY_RESET_PASSWORD,
caller.getPackageName(),
- UserHandle.USER_ALL);
+ userId);
Long currentTokenHandle = mDevicePolicyEngine.getLocalPolicySetByAdmin(
PolicyDefinition.RESET_PASSWORD_TOKEN,
enforcingAdmin,
@@ -19062,7 +19100,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
admin,
MANAGE_DEVICE_POLICY_RESET_PASSWORD,
caller.getPackageName(),
- UserHandle.USER_ALL);
+ userId);
Long currentTokenHandle = mDevicePolicyEngine.getLocalPolicySetByAdmin(
PolicyDefinition.RESET_PASSWORD_TOKEN,
enforcingAdmin,
@@ -19114,7 +19152,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
admin,
MANAGE_DEVICE_POLICY_RESET_PASSWORD,
caller.getPackageName(),
- UserHandle.USER_ALL);
+ userId);
Long currentTokenHandle = mDevicePolicyEngine.getLocalPolicySetByAdmin(
PolicyDefinition.RESET_PASSWORD_TOKEN,
enforcingAdmin,
@@ -19140,10 +19178,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
if (result) {
- DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums.RESET_PASSWORD_WITH_TOKEN)
- .setAdmin(caller.getComponentName())
- .write();
+ if (isPermissionCheckFlagEnabled()) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESET_PASSWORD_WITH_TOKEN)
+ .setAdmin(callerPackageName)
+ .write();
+ } else {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESET_PASSWORD_WITH_TOKEN)
+ .setAdmin(caller.getComponentName())
+ .write();
+ }
}
return result;
}
@@ -22722,6 +22767,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_AUTOFILL,
MANAGE_DEVICE_POLICY_BLUETOOTH,
MANAGE_DEVICE_POLICY_CALLS,
+ MANAGE_DEVICE_POLICY_CAMERA,
MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
MANAGE_DEVICE_POLICY_DISPLAY,
MANAGE_DEVICE_POLICY_FACTORY_RESET,
@@ -22757,7 +22803,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_ACROSS_USERS,
MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
MANAGE_DEVICE_POLICY_APPS_CONTROL,
- MANAGE_DEVICE_POLICY_CAMERA,
MANAGE_DEVICE_POLICY_CERTIFICATES,
MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
MANAGE_DEVICE_POLICY_DEFAULT_SMS,
@@ -22785,7 +22830,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final List<String> ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS =
List.of(
MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
- MANAGE_DEVICE_POLICY_CAMERA,
MANAGE_DEVICE_POLICY_DISPLAY,
MANAGE_DEVICE_POLICY_FUN,
MANAGE_DEVICE_POLICY_LOCK_TASK,
@@ -22796,7 +22840,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
MANAGE_DEVICE_POLICY_SAFE_BOOT,
MANAGE_DEVICE_POLICY_SMS,
- MANAGE_DEVICE_POLICY_STATUS_BAR,
MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER,
MANAGE_DEVICE_POLICY_USERS,
@@ -22817,7 +22860,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
* All the additional permissions granted to a Profile Owner on an affiliated user.
*/
private static final List<String> ADDITIONAL_AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS =
- List.of();
+ List.of(
+ MANAGE_DEVICE_POLICY_STATUS_BAR
+ );
/**
* Combination of {@link PROFILE_OWNER_PERMISSIONS} and
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index d65d366e4476..12a8a75317e5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -84,6 +84,7 @@ final class PolicyEnforcerCallbacks {
? DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT
: grantState;
+ // TODO(b/278710449): stop blocking in the main thread
BlockingCallback callback = new BlockingCallback();
// TODO: remove canAdminGrantSensorPermissions once we expose a new method in
// permissionController that doesn't need it.
diff --git a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
new file mode 100644
index 000000000000..77c339646400
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningShellCommandTest.java
@@ -0,0 +1,244 @@
+/*
+ * 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.server.security.rkp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.security.keymint.DeviceInfo;
+import android.hardware.security.keymint.IRemotelyProvisionedComponent;
+import android.hardware.security.keymint.MacedPublicKey;
+import android.hardware.security.keymint.ProtectedData;
+import android.hardware.security.keymint.RpcHardwareInfo;
+import android.os.Binder;
+import android.os.FileUtils;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+public class RemoteProvisioningShellCommandTest {
+
+ private static class Injector extends RemoteProvisioningShellCommand.Injector {
+
+ private final Map<String, IRemotelyProvisionedComponent> mIrpcs;
+
+ Injector(Map irpcs) {
+ mIrpcs = irpcs;
+ }
+
+ @Override
+ String[] getIrpcNames() {
+ return mIrpcs.keySet().toArray(new String[0]);
+ }
+
+ @Override
+ IRemotelyProvisionedComponent getIrpcBinder(String name) {
+ IRemotelyProvisionedComponent irpc = mIrpcs.get(name);
+ if (irpc == null) {
+ throw new IllegalArgumentException("failed to find " + irpc);
+ }
+ return irpc;
+ }
+ }
+
+ private static class CommandResult {
+ private int mCode;
+ private String mOut;
+ private String mErr;
+
+ CommandResult(int code, String out, String err) {
+ mCode = code;
+ mOut = out;
+ mErr = err;
+ }
+
+ int getCode() {
+ return mCode;
+ }
+
+ String getOut() {
+ return mOut;
+ }
+
+ String getErr() {
+ return mErr;
+ }
+ }
+
+ private static CommandResult exec(
+ RemoteProvisioningShellCommand cmd, String[] args) throws Exception {
+ File in = File.createTempFile("rpsct_in_", null);
+ File out = File.createTempFile("rpsct_out_", null);
+ File err = File.createTempFile("rpsct_err_", null);
+ int code = cmd.exec(
+ new Binder(),
+ new FileInputStream(in).getFD(),
+ new FileOutputStream(out).getFD(),
+ new FileOutputStream(err).getFD(),
+ args);
+ return new CommandResult(
+ code, FileUtils.readTextFile(out, 0, null), FileUtils.readTextFile(err, 0, null));
+ }
+
+ @Test
+ public void list_zeroInstances() throws Exception {
+ RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
+ new Injector(Map.of()));
+ CommandResult res = exec(cmd, new String[] {"list"});
+ assertThat(res.getErr()).isEmpty();
+ assertThat(res.getCode()).isEqualTo(0);
+ assertThat(res.getOut()).isEmpty();
+ }
+
+ @Test
+ public void list_oneInstances() throws Exception {
+ RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
+ new Injector(Map.of("default", mock(IRemotelyProvisionedComponent.class))));
+ CommandResult res = exec(cmd, new String[] {"list"});
+ assertThat(res.getErr()).isEmpty();
+ assertThat(res.getCode()).isEqualTo(0);
+ assertThat(Arrays.asList(res.getOut().split("\n"))).containsExactly("default");
+ }
+
+ @Test
+ public void list_twoInstances() throws Exception {
+ RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
+ new Injector(Map.of(
+ "default", mock(IRemotelyProvisionedComponent.class),
+ "strongbox", mock(IRemotelyProvisionedComponent.class))));
+ CommandResult res = exec(cmd, new String[] {"list"});
+ assertThat(res.getErr()).isEmpty();
+ assertThat(res.getCode()).isEqualTo(0);
+ assertThat(Arrays.asList(res.getOut().split("\n"))).containsExactly("default", "strongbox");
+ }
+
+ @Test
+ public void csr_hwVersion1_withChallenge() throws Exception {
+ IRemotelyProvisionedComponent defaultMock = mock(IRemotelyProvisionedComponent.class);
+ RpcHardwareInfo defaultInfo = new RpcHardwareInfo();
+ defaultInfo.versionNumber = 1;
+ defaultInfo.supportedEekCurve = RpcHardwareInfo.CURVE_25519;
+ when(defaultMock.getHardwareInfo()).thenReturn(defaultInfo);
+ doAnswer(invocation -> {
+ ((DeviceInfo) invocation.getArgument(4)).deviceInfo = new byte[] {0x00};
+ ((ProtectedData) invocation.getArgument(5)).protectedData = new byte[] {0x00};
+ return new byte[] {0x77, 0x77, 0x77, 0x77};
+ }).when(defaultMock).generateCertificateRequest(
+ anyBoolean(), any(), any(), any(), any(), any());
+
+ RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
+ new Injector(Map.of("default", defaultMock)));
+ CommandResult res = exec(cmd, new String[] {
+ "csr", "--challenge", "dGVzdHRlc3R0ZXN0dGVzdA==", "default"});
+ verify(defaultMock).generateCertificateRequest(
+ /*test_mode=*/eq(false),
+ eq(new MacedPublicKey[0]),
+ eq(Base64.getDecoder().decode(RemoteProvisioningShellCommand.EEK_ED25519_BASE64)),
+ eq(new byte[] {
+ 0x74, 0x65, 0x73, 0x74, 0x74, 0x65, 0x73, 0x74,
+ 0x74, 0x65, 0x73, 0x74, 0x74, 0x65, 0x73, 0x74}),
+ any(DeviceInfo.class),
+ any(ProtectedData.class));
+ assertThat(res.getErr()).isEmpty();
+ assertThat(res.getCode()).isEqualTo(0);
+ }
+
+ @Test
+ public void csr_hwVersion2_withChallenge() throws Exception {
+ IRemotelyProvisionedComponent defaultMock = mock(IRemotelyProvisionedComponent.class);
+ RpcHardwareInfo defaultInfo = new RpcHardwareInfo();
+ defaultInfo.versionNumber = 2;
+ defaultInfo.supportedEekCurve = RpcHardwareInfo.CURVE_P256;
+ when(defaultMock.getHardwareInfo()).thenReturn(defaultInfo);
+ doAnswer(invocation -> {
+ ((DeviceInfo) invocation.getArgument(4)).deviceInfo = new byte[] {0x00};
+ ((ProtectedData) invocation.getArgument(5)).protectedData = new byte[] {0x00};
+ return new byte[] {0x77, 0x77, 0x77, 0x77};
+ }).when(defaultMock).generateCertificateRequest(
+ anyBoolean(), any(), any(), any(), any(), any());
+
+ RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
+ new Injector(Map.of("default", defaultMock)));
+ CommandResult res = exec(cmd, new String[] {
+ "csr", "--challenge", "dGVzdHRlc3R0ZXN0dGVzdA==", "default"});
+ verify(defaultMock).generateCertificateRequest(
+ /*test_mode=*/eq(false),
+ eq(new MacedPublicKey[0]),
+ eq(Base64.getDecoder().decode(RemoteProvisioningShellCommand.EEK_P256_BASE64)),
+ eq(new byte[] {
+ 0x74, 0x65, 0x73, 0x74, 0x74, 0x65, 0x73, 0x74,
+ 0x74, 0x65, 0x73, 0x74, 0x74, 0x65, 0x73, 0x74}),
+ any(DeviceInfo.class),
+ any(ProtectedData.class));
+ assertThat(res.getErr()).isEmpty();
+ assertThat(res.getCode()).isEqualTo(0);
+ }
+
+ @Test
+ public void csr_hwVersion3_withoutChallenge() throws Exception {
+ IRemotelyProvisionedComponent defaultMock = mock(IRemotelyProvisionedComponent.class);
+ RpcHardwareInfo defaultInfo = new RpcHardwareInfo();
+ defaultInfo.versionNumber = 3;
+ when(defaultMock.getHardwareInfo()).thenReturn(defaultInfo);
+ when(defaultMock.generateCertificateRequestV2(any(), any()))
+ .thenReturn(new byte[] {0x68, 0x65, 0x6c, 0x6c, 0x6f});
+
+ RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
+ new Injector(Map.of("default", defaultMock)));
+ CommandResult res = exec(cmd, new String[] {"csr", "default"});
+ verify(defaultMock).generateCertificateRequestV2(new MacedPublicKey[0], new byte[0]);
+ assertThat(res.getErr()).isEmpty();
+ assertThat(res.getCode()).isEqualTo(0);
+ assertThat(res.getOut()).isEqualTo("aGVsbG8=\n");
+ }
+
+ @Test
+ public void csr_hwVersion3_withChallenge() throws Exception {
+ IRemotelyProvisionedComponent defaultMock = mock(IRemotelyProvisionedComponent.class);
+ RpcHardwareInfo defaultInfo = new RpcHardwareInfo();
+ defaultInfo.versionNumber = 3;
+ when(defaultMock.getHardwareInfo()).thenReturn(defaultInfo);
+ when(defaultMock.generateCertificateRequestV2(any(), any()))
+ .thenReturn(new byte[] {0x68, 0x69});
+
+ RemoteProvisioningShellCommand cmd = new RemoteProvisioningShellCommand(
+ new Injector(Map.of("default", defaultMock)));
+ CommandResult res = exec(cmd, new String[] {"csr", "--challenge", "dHJpYWw=", "default"});
+ verify(defaultMock).generateCertificateRequestV2(
+ new MacedPublicKey[0], new byte[] {0x74, 0x72, 0x69, 0x61, 0x6c});
+ assertThat(res.getErr()).isEmpty();
+ assertThat(res.getCode()).isEqualTo(0);
+ assertThat(res.getOut()).isEqualTo("aGk=\n");
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index ca857f121624..c4aa0bbc24b5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -445,7 +445,7 @@ public final class DisplayPowerController2Test {
}
@Test
- public void testDisplayBrightnessFollowersRemoval() {
+ public void testDisplayBrightnessFollowersRemoval_RemoveSingleFollower() {
DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID,
FOLLOWER_UNIQUE_ID);
DisplayPowerControllerHolder secondFollowerDpc = createDisplayPowerController(
@@ -520,6 +520,78 @@ public final class DisplayPowerController2Test {
}
@Test
+ public void testDisplayBrightnessFollowersRemoval_RemoveAllFollowers() {
+ DisplayPowerControllerHolder followerHolder =
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
+ DisplayPowerControllerHolder secondFollowerHolder =
+ createDisplayPowerController(SECOND_FOLLOWER_DISPLAY_ID,
+ SECOND_FOLLOWER_UNIQUE_DISPLAY_ID);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ followerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ secondFollowerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
+ ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
+
+ // Set the initial brightness on the DPCs we're going to remove so we have a fixed value for
+ // it to return to.
+ listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(followerHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener followerListener = listenerCaptor.getValue();
+ listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(secondFollowerHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener secondFollowerListener =
+ listenerCaptor.getValue();
+ final float initialFollowerBrightness = 0.3f;
+ when(followerHolder.brightnessSetting.getBrightness()).thenReturn(
+ initialFollowerBrightness);
+ when(secondFollowerHolder.brightnessSetting.getBrightness()).thenReturn(
+ initialFollowerBrightness);
+ followerListener.onBrightnessChanged(initialFollowerBrightness);
+ secondFollowerListener.onBrightnessChanged(initialFollowerBrightness);
+ advanceTime(1);
+ verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+ verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+
+ mHolder.dpc.addDisplayBrightnessFollower(followerHolder.dpc);
+ mHolder.dpc.addDisplayBrightnessFollower(secondFollowerHolder.dpc);
+ clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
+
+ // Validate both followers are correctly registered and receiving brightness updates
+ float brightness = 0.6f;
+ float nits = 600;
+ when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
+ when(followerHolder.automaticBrightnessController.convertToFloatScale(nits))
+ .thenReturn(brightness);
+ when(secondFollowerHolder.automaticBrightnessController.convertToFloatScale(nits))
+ .thenReturn(brightness);
+ when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
+ listener.onBrightnessChanged(brightness);
+ advanceTime(1); // Send messages, run updatePowerState
+ verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+ verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+ verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+
+ clearInvocations(mHolder.animator, followerHolder.animator, secondFollowerHolder.animator);
+
+ // Stop the lead DPC and validate that the followers go back to their original brightness.
+ mHolder.dpc.stop();
+ advanceTime(1);
+ verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+ verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+ clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
+ }
+
+ @Test
public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
// We should still set screen state for the default display
DisplayPowerRequest dpr = new DisplayPowerRequest();
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index 0b97c5cb6ca1..415adbbac91e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -91,7 +91,7 @@ public final class DisplayPowerControllerTest {
private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY;
private static final String UNIQUE_ID = "unique_id_test123";
private static final int FOLLOWER_DISPLAY_ID = DISPLAY_ID + 1;
- private static final String FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_456";
+ private static final String FOLLOWER_UNIQUE_ID = "unique_id_456";
private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1;
private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
private static final float PROX_SENSOR_MAX_RANGE = 5;
@@ -279,7 +279,7 @@ public final class DisplayPowerControllerTest {
@Test
public void testProximitySensorListenerNotRegisteredForNonDefaultDisplay() {
DisplayPowerControllerHolder followerDpc =
- createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_DISPLAY_ID);
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
// send a display power request
@@ -298,7 +298,7 @@ public final class DisplayPowerControllerTest {
@Test
public void testDisplayBrightnessFollowers_BothDpcsSupportNits() {
DisplayPowerControllerHolder followerDpc =
- createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_DISPLAY_ID);
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
DisplayPowerRequest dpr = new DisplayPowerRequest();
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -344,7 +344,7 @@ public final class DisplayPowerControllerTest {
@Test
public void testDisplayBrightnessFollowers_FollowerDoesNotSupportNits() {
DisplayPowerControllerHolder followerDpc =
- createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_DISPLAY_ID);
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
DisplayPowerRequest dpr = new DisplayPowerRequest();
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -372,7 +372,7 @@ public final class DisplayPowerControllerTest {
@Test
public void testDisplayBrightnessFollowers_LeadDpcDoesNotSupportNits() {
DisplayPowerControllerHolder followerDpc =
- createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_DISPLAY_ID);
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
DisplayPowerRequest dpr = new DisplayPowerRequest();
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -398,7 +398,7 @@ public final class DisplayPowerControllerTest {
@Test
public void testDisplayBrightnessFollowers_NeitherDpcSupportsNits() {
DisplayPowerControllerHolder followerDpc =
- createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_DISPLAY_ID);
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
DisplayPowerRequest dpr = new DisplayPowerRequest();
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -449,9 +449,9 @@ public final class DisplayPowerControllerTest {
}
@Test
- public void testDisplayBrightnessFollowersRemoval() {
+ public void testDisplayBrightnessFollowersRemoval_RemoveSingleFollower() {
DisplayPowerControllerHolder followerHolder =
- createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_DISPLAY_ID);
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
DisplayPowerControllerHolder secondFollowerHolder =
createDisplayPowerController(SECOND_FOLLOWER_DISPLAY_ID,
SECOND_FOLLOWER_UNIQUE_DISPLAY_ID);
@@ -525,6 +525,78 @@ public final class DisplayPowerControllerTest {
}
@Test
+ public void testDisplayBrightnessFollowersRemoval_RemoveAllFollowers() {
+ DisplayPowerControllerHolder followerHolder =
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
+ DisplayPowerControllerHolder secondFollowerHolder =
+ createDisplayPowerController(SECOND_FOLLOWER_DISPLAY_ID,
+ SECOND_FOLLOWER_UNIQUE_DISPLAY_ID);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ followerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ secondFollowerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
+ ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
+
+ // Set the initial brightness on the DPCs we're going to remove so we have a fixed value for
+ // it to return to.
+ listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(followerHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener followerListener = listenerCaptor.getValue();
+ listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(secondFollowerHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener secondFollowerListener =
+ listenerCaptor.getValue();
+ final float initialFollowerBrightness = 0.3f;
+ when(followerHolder.brightnessSetting.getBrightness()).thenReturn(
+ initialFollowerBrightness);
+ when(secondFollowerHolder.brightnessSetting.getBrightness()).thenReturn(
+ initialFollowerBrightness);
+ followerListener.onBrightnessChanged(initialFollowerBrightness);
+ secondFollowerListener.onBrightnessChanged(initialFollowerBrightness);
+ advanceTime(1);
+ verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+ verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+
+ mHolder.dpc.addDisplayBrightnessFollower(followerHolder.dpc);
+ mHolder.dpc.addDisplayBrightnessFollower(secondFollowerHolder.dpc);
+ clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
+
+ // Validate both followers are correctly registered and receiving brightness updates
+ float brightness = 0.6f;
+ float nits = 600;
+ when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
+ when(followerHolder.automaticBrightnessController.convertToFloatScale(nits))
+ .thenReturn(brightness);
+ when(secondFollowerHolder.automaticBrightnessController.convertToFloatScale(nits))
+ .thenReturn(brightness);
+ when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
+ listener.onBrightnessChanged(brightness);
+ advanceTime(1); // Send messages, run updatePowerState
+ verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+ verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+ verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+
+ clearInvocations(mHolder.animator, followerHolder.animator, secondFollowerHolder.animator);
+
+ // Stop the lead DPC and validate that the followers go back to their original brightness.
+ mHolder.dpc.stop();
+ advanceTime(1);
+ verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+ verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+ clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
+ }
+
+ @Test
public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
// We should still set screen state for the default display
DisplayPowerRequest dpr = new DisplayPowerRequest();
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
new file mode 100644
index 000000000000..17fba9f43707
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.server.display;
+
+import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.when;
+
+import android.hardware.display.DisplayManager;
+import android.provider.Settings;
+import android.testing.TestableContext;
+import android.view.Display;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.display.RefreshRateSettingsUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RefreshRateSettingsUtilsTest {
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
+ @Mock
+ private DisplayManager mDisplayManagerMock;
+ @Mock
+ private Display mDisplayMock;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext.addMockSystemService(DisplayManager.class, mDisplayManagerMock);
+
+ Display.Mode[] modes = new Display.Mode[] {
+ new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
+ /* refreshRate= */ 60),
+ new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
+ /* refreshRate= */ 120),
+ new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
+ /* refreshRate= */ 90)
+ };
+
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
+ when(mDisplayMock.getSupportedModes()).thenReturn(modes);
+ }
+
+ @Test
+ public void testFindHighestRefreshRateForDefaultDisplay() {
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null);
+ assertEquals(DEFAULT_REFRESH_RATE,
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
+ /* delta= */ 0);
+
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
+ assertEquals(120,
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
+ /* delta= */ 0);
+ }
+
+ @Test
+ public void testGetMinRefreshRate() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.FORCE_PEAK_REFRESH_RATE, -1);
+ assertEquals(0, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0);
+
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.FORCE_PEAK_REFRESH_RATE, 0);
+ assertEquals(0, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0);
+
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.FORCE_PEAK_REFRESH_RATE, 1);
+ assertEquals(120, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0);
+ }
+
+ @Test
+ public void testGetPeakRefreshRate() {
+ float defaultPeakRefreshRate = 100;
+
+ Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, -1);
+ assertEquals(defaultPeakRefreshRate,
+ RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate),
+ /* delta= */ 0);
+
+ Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, 0);
+ assertEquals(DEFAULT_REFRESH_RATE,
+ RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate),
+ /* delta= */ 0);
+
+ Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, 1);
+ assertEquals(120,
+ RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate),
+ /* delta= */ 0);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/OWNERS b/services/tests/mockingservicestests/src/com/android/server/rollback/OWNERS
new file mode 100644
index 000000000000..daa02111f71f
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/OWNERS
@@ -0,0 +1,3 @@
+ancr@google.com
+harshitmahajan@google.com
+robertogil@google.com
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
index a14073006c31..35d4ffdd94d0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
@@ -145,7 +145,7 @@ public class RollbackPackageHealthObserverTest {
observer.onHealthCheckFailed(null,
PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1));
// non-native crash for the package
- assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_60,
observer.onHealthCheckFailed(testFailedPackage,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1));
// non-native crash for a different package
diff --git a/services/tests/servicestests/res/xml/irq_device_map_3.xml b/services/tests/servicestests/res/xml/irq_device_map_3.xml
index 7e2529aa0090..fd55428c48df 100644
--- a/services/tests/servicestests/res/xml/irq_device_map_3.xml
+++ b/services/tests/servicestests/res/xml/irq_device_map_3.xml
@@ -26,4 +26,10 @@
<device name="test.sound_trigger.device">
<subsystem>Sound_trigger</subsystem>
</device>
+ <device name="test.cellular_data.device">
+ <subsystem>Cellular_data</subsystem>
+ </device>
+ <device name="test.sensor.device">
+ <subsystem>Sensor</subsystem>
+ </device>
</irq-device-map> \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index f1ad577fde88..a01c7bdd5144 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -200,7 +200,7 @@ public class FullScreenMagnificationControllerTest {
assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_0));
assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_1));
- verify(mMockThumbnail, times(2)).hideThumbNail();
+ verify(mMockThumbnail, times(2)).hideThumbnail();
}
@Test
@@ -538,7 +538,10 @@ public class FullScreenMagnificationControllerTest {
mConfigCaptor.capture());
assertConfigEquals(config, mConfigCaptor.getValue());
- verify(mMockThumbnail).setThumbNailBounds(any(), anyFloat(), anyFloat(), anyFloat());
+ // The first time is triggered when the thumbnail is just created.
+ // The second time is triggered when the magnification region changed.
+ verify(mMockThumbnail, times(2)).setThumbnailBounds(
+ any(), anyFloat(), anyFloat(), anyFloat());
}
@Test
@@ -909,7 +912,7 @@ public class FullScreenMagnificationControllerTest {
verifyNoMoreInteractions(mMockWindowManager);
verify(mMockThumbnail)
- .updateThumbNail(eq(scale), eq(startCenter.x), eq(startCenter.y));
+ .updateThumbnail(eq(scale), eq(startCenter.x), eq(startCenter.y));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
index 60c8148180da..3baa102b882b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
@@ -66,14 +66,14 @@ public class MagnificationThumbnailTest {
@Test
public void updateThumbnailShows() {
- runOnMainSync(() -> mMagnificationThumbnail.updateThumbNail(
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
/* scale= */ 2f,
/* centerX= */ 5,
/* centerY= */ 10
));
idle();
- runOnMainSync(() -> mMagnificationThumbnail.updateThumbNail(
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
/* scale= */ 2.2f,
/* centerX= */ 15,
/* centerY= */ 50
@@ -86,7 +86,7 @@ public class MagnificationThumbnailTest {
@Test
public void updateThumbnailLingersThenHidesAfterTimeout() throws InterruptedException {
- runOnMainSync(() -> mMagnificationThumbnail.updateThumbNail(
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
/* scale= */ 2f,
/* centerX= */ 5,
/* centerY= */ 10
@@ -103,14 +103,14 @@ public class MagnificationThumbnailTest {
@Test
public void hideThumbnailRemoves() throws InterruptedException {
- runOnMainSync(() -> mMagnificationThumbnail.updateThumbNail(
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
/* scale= */ 2f,
/* centerX= */ 5,
/* centerY= */ 10
));
idle();
- runOnMainSync(() -> mMagnificationThumbnail.hideThumbNail());
+ runOnMainSync(() -> mMagnificationThumbnail.hideThumbnail());
idle();
// Wait for the fade out animation
@@ -122,10 +122,10 @@ public class MagnificationThumbnailTest {
@Test
public void hideShowHideShowHideRemoves() throws InterruptedException {
- runOnMainSync(() -> mMagnificationThumbnail.hideThumbNail());
+ runOnMainSync(() -> mMagnificationThumbnail.hideThumbnail());
idle();
- runOnMainSync(() -> mMagnificationThumbnail.updateThumbNail(
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
/* scale= */ 2f,
/* centerX= */ 5,
/* centerY= */ 10
@@ -135,17 +135,17 @@ public class MagnificationThumbnailTest {
// Wait for the fade in animation
Thread.sleep(200L);
- runOnMainSync(() -> mMagnificationThumbnail.hideThumbNail());
+ runOnMainSync(() -> mMagnificationThumbnail.hideThumbnail());
idle();
- runOnMainSync(() -> mMagnificationThumbnail.updateThumbNail(
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
/* scale= */ 2f,
/* centerX= */ 5,
/* centerY= */ 10
));
idle();
- runOnMainSync(() -> mMagnificationThumbnail.hideThumbNail());
+ runOnMainSync(() -> mMagnificationThumbnail.hideThumbnail());
idle();
@@ -158,7 +158,7 @@ public class MagnificationThumbnailTest {
@Test
public void hideWithoutShowDoesNothing() throws InterruptedException {
- runOnMainSync(() -> mMagnificationThumbnail.hideThumbNail());
+ runOnMainSync(() -> mMagnificationThumbnail.hideThumbnail());
idle();
// Wait for the fade out animation
@@ -172,7 +172,7 @@ public class MagnificationThumbnailTest {
@Test
public void whenHidden_setBoundsDoesNotShow() throws InterruptedException {
- runOnMainSync(() -> mMagnificationThumbnail.setThumbNailBounds(
+ runOnMainSync(() -> mMagnificationThumbnail.setThumbnailBounds(
new Rect(),
/* scale= */ 2f,
/* centerX= */ 5,
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index ab8f3f2279fe..d12741ac8bd6 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -211,8 +211,7 @@ public class UserControllerTest {
@Test
public void testStartUser_foreground() {
mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
- verify(mInjector.getWindowManager()).startFreezingScreen(anyInt(), anyInt());
- verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+ verify(mInjector, never()).dismissUserSwitchingDialog(any());
verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(anyBoolean());
verify(mInjector.getWindowManager()).setSwitchingUser(true);
verify(mInjector).clearAllLockedTasks(anyString());
@@ -224,7 +223,8 @@ public class UserControllerTest {
public void testStartUser_background() {
boolean started = mUserController.startUser(TEST_USER_ID, USER_START_MODE_BACKGROUND);
assertWithMessage("startUser(%s, foreground=false)", TEST_USER_ID).that(started).isTrue();
- verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+ verify(mInjector, never()).showUserSwitchingDialog(
+ any(), any(), anyString(), anyString(), any());
verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
verify(mInjector, never()).clearAllLockedTasks(anyString());
startBackgroundUserAssertions();
@@ -276,7 +276,8 @@ public class UserControllerTest {
assertWithMessage("startUserOnDisplay(%s, %s)", TEST_USER_ID, 42).that(started).isTrue();
verifyUserAssignedToDisplay(TEST_USER_ID, 42);
- verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+ verify(mInjector, never()).showUserSwitchingDialog(
+ any(), any(), anyString(), anyString(), any());
verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
verify(mInjector, never()).clearAllLockedTasks(anyString());
startBackgroundUserAssertions();
@@ -288,8 +289,9 @@ public class UserControllerTest {
/* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
- verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
- verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+ verify(mInjector, never()).showUserSwitchingDialog(
+ any(), any(), anyString(), anyString(), any());
+ verify(mInjector, never()).dismissUserSwitchingDialog(any());
verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
startForegroundUserAssertions();
}
@@ -310,7 +312,8 @@ public class UserControllerTest {
// Make sure no intents have been fired for pre-created users.
assertTrue(mInjector.mSentIntents.isEmpty());
- verify(mInjector.getWindowManager(), never()).startFreezingScreen(anyInt(), anyInt());
+ verify(mInjector, never()).showUserSwitchingDialog(
+ any(), any(), anyString(), anyString(), any());
verify(mInjector.getWindowManager(), never()).setSwitchingUser(anyBoolean());
verify(mInjector, never()).clearAllLockedTasks(anyString());
@@ -442,7 +445,7 @@ public class UserControllerTest {
// Verify that continueUserSwitch worked as expected
continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
verify(mInjector, times(0)).dismissKeyguard(any());
- verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
+ verify(mInjector, times(1)).dismissUserSwitchingDialog(any());
continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false);
verifySystemUserVisibilityChangesNeverNotified();
}
@@ -463,7 +466,7 @@ public class UserControllerTest {
// Verify that continueUserSwitch worked as expected
continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
verify(mInjector, times(1)).dismissKeyguard(any());
- verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
+ verify(mInjector, times(1)).dismissUserSwitchingDialog(any());
continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false);
verifySystemUserVisibilityChangesNeverNotified();
}
@@ -483,7 +486,7 @@ public class UserControllerTest {
mInjector.mHandler.clearAllRecordedMessages();
// Verify that continueUserSwitch worked as expected
continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
- verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
+ verify(mInjector, never()).dismissUserSwitchingDialog(any());
continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false);
}
@@ -985,8 +988,7 @@ public class UserControllerTest {
mInjector.mHandler.clearAllRecordedMessages();
// Verify that continueUserSwitch worked as expected
continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
- verify(mInjector.getWindowManager(), times(expectedNumberOfCalls))
- .stopFreezingScreen();
+ verify(mInjector, times(expectedNumberOfCalls)).dismissUserSwitchingDialog(any());
continueUserSwitchAssertions(oldUserId, newUserId, expectOldUserStopping);
}
@@ -1189,6 +1191,22 @@ public class UserControllerTest {
}
@Override
+ void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser,
+ String switchingFromSystemUserMessage, String switchingToSystemUserMessage,
+ Runnable onShown) {
+ if (onShown != null) {
+ onShown.run();
+ }
+ }
+
+ @Override
+ void dismissUserSwitchingDialog(Runnable onDismissed) {
+ if (onDismissed != null) {
+ onDismissed.run();
+ }
+ }
+
+ @Override
protected LockPatternUtils getLockPatternUtils() {
return mLockPatternUtilsMock;
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index dad9fe8648b2..31599eed539d 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -74,7 +74,7 @@ public class AudioDeviceBrokerTest {
mSpyDevInventory = spy(new AudioDeviceInventory(mSpyAudioSystem));
mSpySystemServer = spy(new NoOpSystemServerAdapter());
mAudioDeviceBroker = new AudioDeviceBroker(mContext, mMockAudioService, mSpyDevInventory,
- mSpySystemServer);
+ mSpySystemServer, mSpyAudioSystem);
mSpyDevInventory.setDeviceBroker(mAudioDeviceBroker);
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java
index f26c7e63a273..9d84a074dd41 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AuthSessionCoordinatorTest.java
@@ -135,7 +135,7 @@ public class AuthSessionCoordinatorTest {
}
@Test
- public void testUserCanAuthDuringLockoutOfSameSession() {
+ public void testUserLockedDuringLockoutOfSameSession() {
mCoordinator.resetLockoutFor(PRIMARY_USER, BIOMETRIC_STRONG, 0 /* requestId */);
assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
@@ -151,9 +151,9 @@ public class AuthSessionCoordinatorTest {
0 /* requestId */);
assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
- LockoutTracker.LOCKOUT_NONE);
+ LockoutTracker.LOCKOUT_PERMANENT);
assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
- LockoutTracker.LOCKOUT_NONE);
+ LockoutTracker.LOCKOUT_PERMANENT);
assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_NONE);
}
@@ -191,9 +191,9 @@ public class AuthSessionCoordinatorTest {
0 /* requestId */);
assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isEqualTo(
- LockoutTracker.LOCKOUT_NONE);
+ LockoutTracker.LOCKOUT_PERMANENT);
assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_WEAK)).isEqualTo(
- LockoutTracker.LOCKOUT_NONE);
+ LockoutTracker.LOCKOUT_PERMANENT);
assertThat(mCoordinator.getLockoutStateFor(PRIMARY_USER, BIOMETRIC_STRONG)).isEqualTo(
LockoutTracker.LOCKOUT_NONE);
diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionServiceTest.java
new file mode 100644
index 000000000000..bacf2568d9ca
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionServiceTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.server.companion.datatransfer.contextsync;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.platform.test.annotations.Presubmit;
+import android.telecom.PhoneAccount;
+import android.testing.AndroidTestingRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+public class CallMetadataSyncConnectionServiceTest {
+
+ private CallMetadataSyncConnectionService mSyncConnectionService;
+
+ @Before
+ public void setUp() throws Exception {
+ mSyncConnectionService = new CallMetadataSyncConnectionService() {
+ @Override
+ public String getPackageName() {
+ return "android";
+ }
+ };
+ }
+
+ @Test
+ public void createPhoneAccount_success() {
+ final PhoneAccount phoneAccount = mSyncConnectionService.createPhoneAccount(
+ new CallMetadataSyncConnectionService.PhoneAccountHandleIdentifier(/*
+ associationId= */
+ 0, "com.google.test"), "Test App");
+ assertWithMessage("Could not create phone account").that(phoneAccount).isNotNull();
+ }
+
+ @Test
+ public void createPhoneAccount_alreadyExists_doesNotCreateAnother() {
+ final PhoneAccount phoneAccount = mSyncConnectionService.createPhoneAccount(
+ new CallMetadataSyncConnectionService.PhoneAccountHandleIdentifier(/*
+ associationId= */
+ 0, "com.google.test"), "Test App");
+ final PhoneAccount phoneAccount2 = mSyncConnectionService.createPhoneAccount(
+ new CallMetadataSyncConnectionService.PhoneAccountHandleIdentifier(/*
+ associationId= */
+ 0, "com.google.test"), "Test App #2");
+ assertWithMessage("Could not create phone account").that(phoneAccount).isNotNull();
+ assertWithMessage("Unexpectedly created second phone account").that(phoneAccount2).isNull();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java
new file mode 100644
index 000000000000..c5a9af7d909d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.server.companion.datatransfer.contextsync;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.testing.AndroidTestingRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+public class CallMetadataSyncDataTest {
+
+ @Test
+ public void call_writeToParcel_fromParcel_reconstructsSuccessfully() {
+ final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call();
+ final long id = 5;
+ final String callerId = "callerId";
+ final byte[] appIcon = "appIcon".getBytes();
+ final String appName = "appName";
+ final String appIdentifier = "com.google.test";
+ final int status = 1;
+ final int control1 = 2;
+ final int control2 = 3;
+ call.setId(id);
+ call.setCallerId(callerId);
+ call.setAppIcon(appIcon);
+ call.setAppName(appName);
+ call.setAppIdentifier(appIdentifier);
+ call.setStatus(status);
+ call.addControl(control1);
+ call.addControl(control2);
+
+ Parcel parcel = Parcel.obtain();
+ call.writeToParcel(parcel, /* flags= */ 0);
+ parcel.setDataPosition(0);
+ final CallMetadataSyncData.Call reconstructedCall = CallMetadataSyncData.Call.fromParcel(
+ parcel);
+
+ assertThat(reconstructedCall.getId()).isEqualTo(id);
+ assertThat(reconstructedCall.getCallerId()).isEqualTo(callerId);
+ assertThat(reconstructedCall.getAppIcon()).isEqualTo(appIcon);
+ assertThat(reconstructedCall.getAppName()).isEqualTo(appName);
+ assertThat(reconstructedCall.getAppIdentifier()).isEqualTo(appIdentifier);
+ assertThat(reconstructedCall.getStatus()).isEqualTo(status);
+ assertThat(reconstructedCall.getControls()).containsExactly(control1, control2);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index a4a3e363ab4d..c8c1d6f0f7ed 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -1160,6 +1160,7 @@ public class VirtualDeviceManagerServiceTest {
final int fd = 1;
final int keyCode = KeyEvent.KEYCODE_A;
final int action = VirtualKeyEvent.ACTION_UP;
+ final long eventTimeNanos = 5000L;
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_KEYBOARD, DISPLAY_ID_1, PHYS,
DEVICE_NAME_1, INPUT_DEVICE_ID);
@@ -1167,8 +1168,9 @@ public class VirtualDeviceManagerServiceTest {
mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder()
.setKeyCode(keyCode)
.setAction(action)
+ .setEventTimeNanos(eventTimeNanos)
.build());
- verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action);
+ verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action, eventTimeNanos);
}
@Test
@@ -1188,14 +1190,17 @@ public class VirtualDeviceManagerServiceTest {
final int fd = 1;
final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK;
final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
+ final long eventTimeNanos = 5000L;
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS,
DEVICE_NAME_1, INPUT_DEVICE_ID);
doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
.setButtonCode(buttonCode)
- .setAction(action).build());
- verify(mNativeWrapperMock).writeButtonEvent(fd, buttonCode, action);
+ .setAction(action)
+ .setEventTimeNanos(eventTimeNanos)
+ .build());
+ verify(mNativeWrapperMock).writeButtonEvent(fd, buttonCode, action, eventTimeNanos);
}
@Test
@@ -1229,13 +1234,17 @@ public class VirtualDeviceManagerServiceTest {
final int fd = 1;
final float x = -0.2f;
final float y = 0.7f;
+ final long eventTimeNanos = 5000L;
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
INPUT_DEVICE_ID);
doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
- .setRelativeX(x).setRelativeY(y).build());
- verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y);
+ .setRelativeX(x)
+ .setRelativeY(y)
+ .setEventTimeNanos(eventTimeNanos)
+ .build());
+ verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y, eventTimeNanos);
}
@Test
@@ -1270,14 +1279,17 @@ public class VirtualDeviceManagerServiceTest {
final int fd = 1;
final float x = 0.5f;
final float y = 1f;
+ final long eventTimeNanos = 5000L;
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
INPUT_DEVICE_ID);
doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
.setXAxisMovement(x)
- .setYAxisMovement(y).build());
- verify(mNativeWrapperMock).writeScrollEvent(fd, x, y);
+ .setYAxisMovement(y)
+ .setEventTimeNanos(eventTimeNanos)
+ .build());
+ verify(mNativeWrapperMock).writeScrollEvent(fd, x, y, eventTimeNanos);
}
@Test
@@ -1318,6 +1330,7 @@ public class VirtualDeviceManagerServiceTest {
final float x = 100.5f;
final float y = 200.5f;
final int action = VirtualTouchEvent.ACTION_UP;
+ final long eventTimeNanos = 5000L;
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_TOUCHSCREEN, DISPLAY_ID_1, PHYS,
DEVICE_NAME_1, INPUT_DEVICE_ID);
@@ -1327,9 +1340,10 @@ public class VirtualDeviceManagerServiceTest {
.setAction(action)
.setPointerId(pointerId)
.setToolType(toolType)
+ .setEventTimeNanos(eventTimeNanos)
.build());
verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN,
- Float.NaN);
+ Float.NaN, eventTimeNanos);
}
@Test
@@ -1342,6 +1356,7 @@ public class VirtualDeviceManagerServiceTest {
final int action = VirtualTouchEvent.ACTION_UP;
final float pressure = 1.0f;
final float majorAxisSize = 10.0f;
+ final long eventTimeNanos = 5000L;
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_TOUCHSCREEN, DISPLAY_ID_1, PHYS,
DEVICE_NAME_1, INPUT_DEVICE_ID);
@@ -1353,9 +1368,10 @@ public class VirtualDeviceManagerServiceTest {
.setToolType(toolType)
.setPressure(pressure)
.setMajorAxisSize(majorAxisSize)
+ .setEventTimeNanos(eventTimeNanos)
.build());
verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, pressure,
- majorAxisSize);
+ majorAxisSize, eventTimeNanos);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 16aadacb5af6..d85db64b74cd 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3267,31 +3267,31 @@ public class DevicePolicyManagerTest extends DpmTestBase {
reset(getServices().settings);
dpm.setMaximumTimeToLock(admin1, 0);
- verifyScreenTimeoutCall(null, UserHandle.USER_SYSTEM);
+ verifyScreenTimeoutCall(null, CALLER_USER_HANDLE);
verifyStayOnWhilePluggedCleared(false);
reset(getServices().powerManagerInternal);
reset(getServices().settings);
dpm.setMaximumTimeToLock(admin1, 1);
- verifyScreenTimeoutCall(1L, UserHandle.USER_SYSTEM);
+ verifyScreenTimeoutCall(1L, CALLER_USER_HANDLE);
verifyStayOnWhilePluggedCleared(true);
reset(getServices().powerManagerInternal);
reset(getServices().settings);
dpm.setMaximumTimeToLock(admin2, 10);
- verifyScreenTimeoutCall(null, UserHandle.USER_SYSTEM);
+ verifyScreenTimeoutCall(null, CALLER_USER_HANDLE);
verifyStayOnWhilePluggedCleared(false);
reset(getServices().powerManagerInternal);
reset(getServices().settings);
dpm.setMaximumTimeToLock(admin1, 5);
- verifyScreenTimeoutCall(5L, UserHandle.USER_SYSTEM);
+ verifyScreenTimeoutCall(5L, CALLER_USER_HANDLE);
verifyStayOnWhilePluggedCleared(true);
reset(getServices().powerManagerInternal);
reset(getServices().settings);
dpm.setMaximumTimeToLock(admin2, 4);
- verifyScreenTimeoutCall(4L, UserHandle.USER_SYSTEM);
+ verifyScreenTimeoutCall(4L, CALLER_USER_HANDLE);
verifyStayOnWhilePluggedCleared(true);
reset(getServices().powerManagerInternal);
reset(getServices().settings);
@@ -3301,20 +3301,20 @@ public class DevicePolicyManagerTest extends DpmTestBase {
reset(getServices().settings);
dpm.setMaximumTimeToLock(admin2, Long.MAX_VALUE);
- verifyScreenTimeoutCall(Long.MAX_VALUE, UserHandle.USER_SYSTEM);
+ verifyScreenTimeoutCall(Long.MAX_VALUE, CALLER_USER_HANDLE);
verifyStayOnWhilePluggedCleared(true);
reset(getServices().powerManagerInternal);
reset(getServices().settings);
dpm.setMaximumTimeToLock(admin2, 10);
- verifyScreenTimeoutCall(10L, UserHandle.USER_SYSTEM);
+ verifyScreenTimeoutCall(10L, CALLER_USER_HANDLE);
verifyStayOnWhilePluggedCleared(true);
reset(getServices().powerManagerInternal);
reset(getServices().settings);
// There's no restriction; should be set to MAX.
dpm.setMaximumTimeToLock(admin2, 0);
- verifyScreenTimeoutCall(Long.MAX_VALUE, UserHandle.USER_SYSTEM);
+ verifyScreenTimeoutCall(Long.MAX_VALUE, CALLER_USER_HANDLE);
verifyStayOnWhilePluggedCleared(false);
}
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index 89ff2c258c26..5f81869903c3 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -287,25 +287,37 @@ public class BrightnessMappingStrategyTest {
adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f;
}
- // Default is unadjusted
+ // Default
assertEquals(DISPLAY_RANGE_NITS[0], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]),
- 0.0001f /* tolerance */);
+ TOLERANCE);
assertEquals(DISPLAY_RANGE_NITS[1], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]),
- 0.0001f /* tolerance */);
+ TOLERANCE);
+ assertEquals(DISPLAY_RANGE_NITS[0],
+ strategy.convertToAdjustedNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]), TOLERANCE);
+ assertEquals(DISPLAY_RANGE_NITS[1],
+ strategy.convertToAdjustedNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]), TOLERANCE);
- // When adjustment is turned on, adjustment array is used
+ // Adjustment is turned on
strategy.recalculateSplines(true, adjustedNits50p);
+ assertEquals(DISPLAY_RANGE_NITS[0], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]),
+ TOLERANCE);
+ assertEquals(DISPLAY_RANGE_NITS[1], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]),
+ TOLERANCE);
assertEquals(DISPLAY_RANGE_NITS[0] / 2,
- strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]), 0.0001f /* tolerance */);
+ strategy.convertToAdjustedNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]), TOLERANCE);
assertEquals(DISPLAY_RANGE_NITS[1] / 2,
- strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]), 0.0001f /* tolerance */);
+ strategy.convertToAdjustedNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]), TOLERANCE);
- // When adjustment is turned off, adjustment array is ignored
+ // Adjustment is turned off
strategy.recalculateSplines(false, adjustedNits50p);
assertEquals(DISPLAY_RANGE_NITS[0], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]),
- 0.0001f /* tolerance */);
+ TOLERANCE);
assertEquals(DISPLAY_RANGE_NITS[1], strategy.convertToNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]),
- 0.0001f /* tolerance */);
+ TOLERANCE);
+ assertEquals(DISPLAY_RANGE_NITS[0],
+ strategy.convertToAdjustedNits(BACKLIGHT_RANGE_ZERO_TO_ONE[0]), TOLERANCE);
+ assertEquals(DISPLAY_RANGE_NITS[1],
+ strategy.convertToAdjustedNits(BACKLIGHT_RANGE_ZERO_TO_ONE[1]), TOLERANCE);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 3b10db4dceb8..e2a66f03f5ca 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -16,8 +16,6 @@
package com.android.server.display;
-import static android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
-import static android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
@@ -29,6 +27,8 @@ import static com.android.server.display.DisplayDeviceConfig.HDR_PERCENT_OF_SCRE
import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.anyFloat;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
@@ -39,14 +39,10 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.ContextWrapper;
+import android.hardware.display.BrightnessInfo;
import android.os.Binder;
import android.os.Handler;
-import android.os.IThermalEventListener;
-import android.os.IThermalService;
import android.os.Message;
-import android.os.PowerManager;
-import android.os.Temperature;
-import android.os.Temperature.ThrottlingStatus;
import android.os.test.TestLooper;
import android.test.mock.MockContentResolver;
import android.util.MathUtils;
@@ -66,8 +62,6 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -80,7 +74,6 @@ public class HighBrightnessModeControllerTest {
private static final long TIME_WINDOW_MILLIS = 55 * 1000;
private static final long TIME_ALLOWED_IN_WINDOW_MILLIS = 12 * 1000;
private static final long TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS = 5 * 1000;
- private static final int THERMAL_STATUS_LIMIT = PowerManager.THERMAL_STATUS_SEVERE;
private static final boolean ALLOW_IN_LOW_POWER_MODE = false;
private static final float DEFAULT_MIN = 0.01f;
@@ -102,17 +95,13 @@ public class HighBrightnessModeControllerTest {
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
- @Mock IThermalService mThermalServiceMock;
@Mock Injector mInjectorMock;
@Mock HighBrightnessModeController.HdrBrightnessDeviceConfig mHdrBrightnessDeviceConfigMock;
- @Captor ArgumentCaptor<IThermalEventListener> mThermalEventListenerCaptor;
-
private static final HighBrightnessModeData DEFAULT_HBM_DATA =
new HighBrightnessModeData(MINIMUM_LUX, TRANSITION_POINT, TIME_WINDOW_MILLIS,
TIME_ALLOWED_IN_WINDOW_MILLIS, TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS,
- THERMAL_STATUS_LIMIT, ALLOW_IN_LOW_POWER_MODE,
- HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT);
+ ALLOW_IN_LOW_POWER_MODE, HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT);
@Before
public void setUp() {
@@ -125,8 +114,6 @@ public class HighBrightnessModeControllerTest {
mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(resolver);
-
- when(mInjectorMock.getThermalService()).thenReturn(mThermalServiceMock);
}
/////////////////
@@ -321,34 +308,14 @@ public class HighBrightnessModeControllerTest {
}
@Test
- public void testNoHbmInHighThermalState() throws Exception {
+ public void testHbmIsNotTurnedOffInHighThermalState() throws Exception {
final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
- verify(mThermalServiceMock).registerThermalEventListenerWithType(
- mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
- final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
-
- // Set the thermal status too high.
- listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
-
- // Try to go into HBM mode but fail
- hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
- hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- advanceTime(10);
+ // Disabled thermal throttling
+ hbmc.onBrightnessChanged(/*brightness=*/ 1f, /*unthrottledBrightness*/ 1f,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
- assertEquals(HIGH_BRIGHTNESS_MODE_OFF, hbmc.getHighBrightnessMode());
- }
-
- @Test
- public void testHbmTurnsOffInHighThermalState() throws Exception {
- final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
-
- verify(mThermalServiceMock).registerThermalEventListenerWithType(
- mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
- final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
-
- // Set the thermal status tolerable
- listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_LIGHT));
+ assertFalse(hbmc.isThermalThrottlingActive());
// Try to go into HBM mode
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
@@ -357,15 +324,19 @@ public class HighBrightnessModeControllerTest {
assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
- // Set the thermal status too high and verify we're off.
- listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
+ // Enable thermal throttling
+ hbmc.onBrightnessChanged(/*brightness=*/ TRANSITION_POINT - 0.01f,
+ /*unthrottledBrightness*/ 1f, BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL);
advanceTime(10);
- assertEquals(HIGH_BRIGHTNESS_MODE_OFF, hbmc.getHighBrightnessMode());
+ assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
+ assertTrue(hbmc.isThermalThrottlingActive());
- // Set the thermal status low again and verify we're back on.
- listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_SEVERE));
+ // Disabled thermal throttling
+ hbmc.onBrightnessChanged(/*brightness=*/ 1f, /*unthrottledBrightness*/ 1f,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
advanceTime(1);
assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
+ assertFalse(hbmc.isThermalThrottlingActive());
}
@Test
@@ -578,33 +549,6 @@ public class HighBrightnessModeControllerTest {
anyInt());
}
- // Test reporting of thermal throttling when triggered by HighBrightnessModeController's
- // internal thermal throttling.
- @Test
- public void testHbmStats_InternalThermalOff() throws Exception {
- final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock());
- final int displayStatsId = mDisplayUniqueId.hashCode();
-
- verify(mThermalServiceMock).registerThermalEventListenerWithType(
- mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
- final IThermalEventListener thermListener = mThermalEventListenerCaptor.getValue();
-
- hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
- hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
- hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
- advanceTime(1);
- verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
-
- thermListener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL));
- advanceTime(10);
- assertEquals(HIGH_BRIGHTNESS_MODE_OFF, hbmc.getHighBrightnessMode());
- verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT));
- }
-
// Test reporting of thermal throttling when triggered externally through
// HighBrightnessModeController.onBrightnessChanged()
@Test
@@ -617,14 +561,16 @@ public class HighBrightnessModeControllerTest {
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
// Brightness is unthrottled, HBM brightness granted
- hbmc.onBrightnessChanged(hbmBrightness, hbmBrightness, BRIGHTNESS_MAX_REASON_NONE);
+ hbmc.onBrightnessChanged(hbmBrightness, hbmBrightness,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
advanceTime(1);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
// Brightness is thermally throttled, HBM brightness denied (NBM brightness granted)
- hbmc.onBrightnessChanged(nbmBrightness, hbmBrightness, BRIGHTNESS_MAX_REASON_THERMAL);
+ hbmc.onBrightnessChanged(nbmBrightness, hbmBrightness,
+ BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL);
advanceTime(1);
// We expect HBM mode to remain set to sunlight, indicating that HBMC *allows* this mode.
// However, we expect the HBM state reported by HBMC to be off, since external thermal
@@ -784,11 +730,7 @@ public class HighBrightnessModeControllerTest {
mTestLooper.dispatchAll();
}
- private Temperature getSkinTemp(@ThrottlingStatus int status) {
- return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
- }
-
private void hbmcOnBrightnessChanged(HighBrightnessModeController hbmc, float brightness) {
- hbmc.onBrightnessChanged(brightness, brightness, BRIGHTNESS_MAX_REASON_NONE);
+ hbmc.onBrightnessChanged(brightness, brightness, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index ff89be75c5d4..5ea30298890f 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -17,6 +17,8 @@
package com.android.server.display;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -26,6 +28,7 @@ import static org.mockito.Mockito.when;
import android.app.PropertyInvalidatedCache;
import android.graphics.Point;
+import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -47,6 +50,7 @@ public class LogicalDisplayTest {
private static final int LAYER_STACK = 0;
private static final int DISPLAY_WIDTH = 100;
private static final int DISPLAY_HEIGHT = 200;
+ private static final int MODE_ID = 1;
private LogicalDisplay mLogicalDisplay;
private DisplayDevice mDisplayDevice;
@@ -65,6 +69,9 @@ public class LogicalDisplayTest {
mDisplayDeviceInfo.height = DISPLAY_HEIGHT;
mDisplayDeviceInfo.flags = DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
+ mDisplayDeviceInfo.modeId = MODE_ID;
+ mDisplayDeviceInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT, /* refreshRate= */ 60)};
when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(mDisplayDeviceInfo);
// Disable binder caches in this process.
@@ -168,14 +175,34 @@ public class LogicalDisplayTest {
}
@Test
- public void testLayoutLimitedRefreshRateNotClearedAfterUpdate() {
- SurfaceControl.RefreshRateRange refreshRateRange = new SurfaceControl.RefreshRateRange(1,
- 2);
- mLogicalDisplay.updateLayoutLimitedRefreshRateLocked(refreshRateRange);
- mLogicalDisplay.updateDisplayGroupIdLocked(1);
+ public void testUpdateLayoutLimitedRefreshRate() {
+ SurfaceControl.RefreshRateRange layoutLimitedRefreshRate =
+ new SurfaceControl.RefreshRateRange(0, 120);
+ DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked();
+ mLogicalDisplay.updateLayoutLimitedRefreshRateLocked(layoutLimitedRefreshRate);
+ DisplayInfo info2 = mLogicalDisplay.getDisplayInfoLocked();
+ // Display info should only be updated when updateLocked is called
+ assertEquals(info2, info1);
- DisplayInfo result = mLogicalDisplay.getDisplayInfoLocked();
+ mLogicalDisplay.updateLocked(mDeviceRepo);
+ DisplayInfo info3 = mLogicalDisplay.getDisplayInfoLocked();
+ assertNotEquals(info3, info2);
+ assertEquals(layoutLimitedRefreshRate, info3.layoutLimitedRefreshRate);
+ }
- assertEquals(refreshRateRange, result.layoutLimitedRefreshRate);
+ @Test
+ public void testUpdateRefreshRateThermalThrottling() {
+ SparseArray<SurfaceControl.RefreshRateRange> refreshRanges = new SparseArray<>();
+ refreshRanges.put(0, new SurfaceControl.RefreshRateRange(0, 120));
+ DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked();
+ mLogicalDisplay.updateThermalRefreshRateThrottling(refreshRanges);
+ DisplayInfo info2 = mLogicalDisplay.getDisplayInfoLocked();
+ // Display info should only be updated when updateLocked is called
+ assertEquals(info2, info1);
+
+ mLogicalDisplay.updateLocked(mDeviceRepo);
+ DisplayInfo info3 = mLogicalDisplay.getDisplayInfoLocked();
+ assertNotEquals(info3, info2);
+ assertTrue(refreshRanges.contentEquals(info3.thermalRefreshRateThrottling));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index cfb432ab3784..d7b12e031c35 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -279,20 +279,27 @@ public final class DisplayBrightnessControllerTest {
@Test
public void testConvertToNits() {
- float brightness = 0.5f;
- float nits = 300;
+ final float brightness = 0.5f;
+ final float nits = 300;
+ final float adjustedNits = 200;
// ABC is null
assertEquals(-1f, mDisplayBrightnessController.convertToNits(brightness),
/* delta= */ 0);
+ assertEquals(-1f, mDisplayBrightnessController.convertToAdjustedNits(brightness),
+ /* delta= */ 0);
AutomaticBrightnessController automaticBrightnessController =
mock(AutomaticBrightnessController.class);
when(automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
+ when(automaticBrightnessController.convertToAdjustedNits(brightness))
+ .thenReturn(adjustedNits);
mDisplayBrightnessController.setAutomaticBrightnessController(
automaticBrightnessController);
assertEquals(nits, mDisplayBrightnessController.convertToNits(brightness), /* delta= */ 0);
+ assertEquals(adjustedNits, mDisplayBrightnessController.convertToAdjustedNits(brightness),
+ /* delta= */ 0);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 6907145542cc..4cfcee5005b4 100644
--- a/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -84,6 +84,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.display.BrightnessSynchronizer;
+import com.android.internal.display.RefreshRateSettingsUtils;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
import com.android.internal.util.test.FakeSettingsProvider;
@@ -126,7 +127,8 @@ public class DisplayModeDirectorTest {
private static final String TAG = "DisplayModeDirectorTest";
private static final boolean DEBUG = false;
private static final float FLOAT_TOLERANCE = 0.01f;
- private static final int DISPLAY_ID = 0;
+ private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY;
+ private static final int MODE_ID = 1;
private static final float TRANSITION_POINT = 0.763f;
private static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY;
@@ -158,6 +160,9 @@ public class DisplayModeDirectorTest {
LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock);
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+
+ clearSmoothDisplaySetting();
+ clearForcePeakRefreshRateSetting();
}
private DisplayModeDirector createDirectorFromRefreshRateArray(
@@ -919,7 +924,6 @@ public class DisplayModeDirectorTest {
public void testLockFpsForLowZone() throws Exception {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- setPeakRefreshRate(90);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -927,6 +931,7 @@ public class DisplayModeDirectorTest {
config.setRefreshRateInLowZone(90);
config.setLowDisplayBrightnessThresholds(new int[] { 10 });
config.setLowAmbientBrightnessThresholds(new int[] { 20 });
+ config.setDefaultPeakRefreshRate(90);
Sensor lightSensor = createLightSensor();
SensorManager sensorManager = createMockSensorManager(lightSensor);
@@ -977,7 +982,6 @@ public class DisplayModeDirectorTest {
public void testLockFpsForHighZone() throws Exception {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -985,6 +989,7 @@ public class DisplayModeDirectorTest {
config.setRefreshRateInHighZone(60);
config.setHighDisplayBrightnessThresholds(new int[] { 255 });
config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+ config.setDefaultPeakRefreshRate(90);
Sensor lightSensor = createLightSensor();
SensorManager sensorManager = createMockSensorManager(lightSensor);
@@ -1032,16 +1037,123 @@ public class DisplayModeDirectorTest {
}
@Test
+ public void testSmoothDisplay() {
+ float defaultRefreshRate = 60;
+ int defaultPeakRefreshRate = 100;
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ director.getSettingsObserver().setDefaultRefreshRate(defaultRefreshRate);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ final FakeDeviceConfig config = mInjector.getDeviceConfig();
+ config.setDefaultPeakRefreshRate(defaultPeakRefreshRate);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ // Default value of the setting
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ defaultPeakRefreshRate);
+ vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ Float.POSITIVE_INFINITY);
+ vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ defaultRefreshRate);
+
+ setSmoothDisplayEnabled(false);
+
+ vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE);
+ vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ Float.POSITIVE_INFINITY);
+ vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ defaultRefreshRate);
+
+ setSmoothDisplayEnabled(true);
+
+ vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
+ vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ Float.POSITIVE_INFINITY);
+ vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ defaultRefreshRate);
+ }
+
+ @Test
+ public void testForcePeakRefreshRate() {
+ float defaultRefreshRate = 60;
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ director.getSettingsObserver().setDefaultRefreshRate(defaultRefreshRate);
+ director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
+
+ Sensor lightSensor = createLightSensor();
+ SensorManager sensorManager = createMockSensorManager(lightSensor);
+ director.start(sensorManager);
+
+ setForcePeakRefreshRateEnabled(false);
+ setSmoothDisplayEnabled(false);
+
+ Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE);
+ vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
+ /* frameRateHigh= */ Float.POSITIVE_INFINITY);
+ vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ defaultRefreshRate);
+
+ setForcePeakRefreshRateEnabled(true);
+
+ vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
+ vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */
+ RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
+ /* frameRateHigh= */ Float.POSITIVE_INFINITY);
+ vote = director.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE);
+ assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
+ defaultRefreshRate);
+ }
+
+ @Test
public void testSensorRegistration() {
// First, configure brightness zones or DMD won't register for sensor data.
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInHighZone(60);
config.setHighDisplayBrightnessThresholds(new int[] { 255 });
config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+ config.setDefaultPeakRefreshRate(90);
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -2417,10 +2529,10 @@ public class DisplayModeDirectorTest {
config.setRefreshRateInHighZone(60);
config.setHighDisplayBrightnessThresholds(new int[] { 255 });
config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+ config.setDefaultPeakRefreshRate(90);
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
- setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
@@ -2533,6 +2645,33 @@ public class DisplayModeDirectorTest {
assertNull(vote);
}
+ @Test
+ public void testUpdateLayoutLimitedRefreshRate() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
+ director.start(createMockSensorManager());
+
+ ArgumentCaptor<DisplayListener> displayListenerCaptor =
+ ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+ any(Handler.class));
+ DisplayListener displayListener = displayListenerCaptor.getValue();
+
+ float refreshRate = 60;
+ mInjector.mDisplayInfo.layoutLimitedRefreshRate =
+ new RefreshRateRange(refreshRate, refreshRate);
+ displayListener.onDisplayChanged(DISPLAY_ID);
+
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
+ assertVoteForPhysicalRefreshRate(vote, /* refreshRate= */ refreshRate);
+
+ mInjector.mDisplayInfo.layoutLimitedRefreshRate = null;
+ displayListener.onDisplayChanged(DISPLAY_ID);
+
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
+ assertNull(vote);
+ }
+
private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) {
return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
}
@@ -2671,10 +2810,30 @@ public class DisplayModeDirectorTest {
listener.onDisplayChanged(DISPLAY_ID);
}
- private void setPeakRefreshRate(float fps) {
- Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE,
- fps);
- mInjector.notifyPeakRefreshRateChanged();
+ private void setSmoothDisplayEnabled(boolean enabled) {
+ Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY,
+ enabled ? 1 : 0);
+ mInjector.notifySmoothDisplaySettingChanged();
+ waitForIdleSync();
+ }
+
+ private void clearSmoothDisplaySetting() {
+ Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, -1);
+ mInjector.notifySmoothDisplaySettingChanged();
+ waitForIdleSync();
+ }
+
+ private void setForcePeakRefreshRateEnabled(boolean enabled) {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.FORCE_PEAK_REFRESH_RATE, enabled ? 1 : 0);
+ mInjector.notifyForcePeakRefreshRateSettingChanged();
+ waitForIdleSync();
+ }
+
+ private void clearForcePeakRefreshRateSetting() {
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.FORCE_PEAK_REFRESH_RATE, -1);
+ mInjector.notifySmoothDisplaySettingChanged();
waitForIdleSync();
}
@@ -2719,11 +2878,19 @@ public class DisplayModeDirectorTest {
public static class FakesInjector implements DisplayModeDirector.Injector {
private final FakeDeviceConfig mDeviceConfig;
+ private final DisplayInfo mDisplayInfo;
+ private final Display mDisplay;
private ContentObserver mBrightnessObserver;
- private ContentObserver mPeakRefreshRateObserver;
+ private ContentObserver mSmoothDisplaySettingObserver;
+ private ContentObserver mForcePeakRefreshRateSettingObserver;
FakesInjector() {
mDeviceConfig = new FakeDeviceConfig();
+ mDisplayInfo = new DisplayInfo();
+ mDisplayInfo.defaultModeId = MODE_ID;
+ mDisplayInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
+ 800, 600, /* refreshRate= */ 60)};
+ mDisplay = createDisplay(DISPLAY_ID);
}
@NonNull
@@ -2732,22 +2899,37 @@ public class DisplayModeDirectorTest {
}
@Override
- public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+ public void registerSmoothDisplayObserver(@NonNull ContentResolver cr,
+ @NonNull ContentObserver observer) {
+ mSmoothDisplaySettingObserver = observer;
+ }
+
+ @Override
+ public void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
- mPeakRefreshRateObserver = observer;
+ mForcePeakRefreshRateSettingObserver = observer;
}
@Override
+ public void registerDisplayListener(DisplayListener listener, Handler handler) {}
+
+ @Override
public void registerDisplayListener(DisplayListener listener, Handler handler, long flag) {}
@Override
+ public Display getDisplay(int displayId) {
+ return mDisplay;
+ }
+
+ @Override
public Display[] getDisplays() {
- return new Display[] { createDisplay(DISPLAY_ID) };
+ return new Display[] { mDisplay };
}
@Override
public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) {
- return false;
+ displayInfo.copyFrom(mDisplayInfo);
+ return true;
}
@Override
@@ -2771,14 +2953,21 @@ public class DisplayModeDirectorTest {
}
protected Display createDisplay(int id) {
- return new Display(DisplayManagerGlobal.getInstance(), id, new DisplayInfo(),
+ return new Display(DisplayManagerGlobal.getInstance(), id, mDisplayInfo,
ApplicationProvider.getApplicationContext().getResources());
}
- void notifyPeakRefreshRateChanged() {
- if (mPeakRefreshRateObserver != null) {
- mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
- PEAK_REFRESH_RATE_URI);
+ void notifySmoothDisplaySettingChanged() {
+ if (mSmoothDisplaySettingObserver != null) {
+ mSmoothDisplaySettingObserver.dispatchChange(false /*selfChange*/,
+ SMOOTH_DISPLAY_URI);
+ }
+ }
+
+ void notifyForcePeakRefreshRateSettingChanged() {
+ if (mForcePeakRefreshRateSettingObserver != null) {
+ mForcePeakRefreshRateSettingObserver.dispatchChange(false /*selfChange*/,
+ FORCE_PEAK_REFRESH_RATE_URI);
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java b/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
index fd1889ca0982..13540d60b930 100644
--- a/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
@@ -253,7 +253,7 @@ public class SkinThermalStatusObserverTest {
public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) {
SparseArray<SurfaceControl.RefreshRateRange> config = mOverriddenConfig.get(displayId);
if (config != null) {
- displayInfo.refreshRateThermalThrottling = config;
+ displayInfo.thermalRefreshRateThrottling = config;
return true;
}
return false;
diff --git a/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java b/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java
index 1ef11974292b..d5ad815d3cdb 100644
--- a/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityTaskManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
@@ -54,6 +55,10 @@ public class DreamControllerTest {
private DreamController.Listener mListener;
@Mock
private Context mContext;
+
+ @Mock
+ private ActivityTaskManager mActivityTaskManager;
+
@Mock
private IBinder mIBinder;
@Mock
@@ -80,6 +85,10 @@ public class DreamControllerTest {
when(mIDreamService.asBinder()).thenReturn(mIBinder);
when(mIBinder.queryLocalInterface(anyString())).thenReturn(mIDreamService);
when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+ when(mContext.getSystemService(Context.ACTIVITY_TASK_SERVICE))
+ .thenReturn(mActivityTaskManager);
+ when(mContext.getSystemServiceName(ActivityTaskManager.class))
+ .thenReturn(Context.ACTIVITY_TASK_SERVICE);
mToken = new Binder();
mDreamName = ComponentName.unflattenFromString("dream");
@@ -104,6 +113,37 @@ public class DreamControllerTest {
}
@Test
+ public void startDream_dreamListenerNotified() {
+ // Call dream controller to start dreaming.
+ mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
+ 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
+
+ // Mock service connected.
+ final ServiceConnection serviceConnection = captureServiceConnection();
+ serviceConnection.onServiceConnected(mDreamName, mIBinder);
+ mLooper.dispatchAll();
+
+ // Verify that dream service is called to attach.
+ verify(mListener).onDreamStarted(any());
+ }
+
+ @Test
+ public void stopDream_dreamListenerNotified() {
+ // Start dream.
+ mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
+ 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
+ captureServiceConnection().onServiceConnected(mDreamName, mIBinder);
+ mLooper.dispatchAll();
+
+ // Stop dream.
+ mDreamController.stopDream(true /*immediate*/, "test stop dream" /*reason*/);
+ mLooper.dispatchAll();
+
+ // Verify that dream service is called to detach.
+ verify(mListener).onDreamStopped(any());
+ }
+
+ @Test
public void startDream_attachOnServiceConnectedInPreviewMode() throws RemoteException {
// Call dream controller to start dreaming.
mDreamController.startDream(mToken, mDreamName, true /*isPreview*/, false /*doze*/,
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 136507d429fc..726a4e285d68 100644
--- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -57,6 +57,8 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.CountDownLatch;
+
/**
* Tests for {@link com.android.server.power.hint.HintManagerService}.
*
@@ -149,29 +151,28 @@ public class HintManagerServiceTest {
// Set session to background and calling updateHintAllowed() would invoke pause();
service.mUidObserver.onUidStateChanged(
a.mUid, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
- final Object sync = new Object();
+
+ // Using CountDownLatch to ensure above onUidStateChanged() job was digested.
+ final CountDownLatch latch = new CountDownLatch(1);
FgThread.getHandler().post(() -> {
- synchronized (sync) {
- sync.notify();
- }
+ latch.countDown();
});
- synchronized (sync) {
- sync.wait();
- }
+ latch.await();
+
assumeFalse(a.updateHintAllowed());
verify(mNativeWrapperMock, times(1)).halPauseHintSession(anyLong());
// Set session to foreground and calling updateHintAllowed() would invoke resume();
service.mUidObserver.onUidStateChanged(
a.mUid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
+
+ // Using CountDownLatch to ensure above onUidStateChanged() job was digested.
+ final CountDownLatch latch2 = new CountDownLatch(1);
FgThread.getHandler().post(() -> {
- synchronized (sync) {
- sync.notify();
- }
+ latch2.countDown();
});
- synchronized (sync) {
- sync.wait();
- }
+ latch2.await();
+
assumeTrue(a.updateHintAllowed());
verify(mNativeWrapperMock, times(1)).halResumeHintSession(anyLong());
}
@@ -237,15 +238,14 @@ public class HintManagerServiceTest {
// Set session to background, then the duration would not be updated.
service.mUidObserver.onUidStateChanged(
a.mUid, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
- final Object sync = new Object();
+
+ // Using CountDownLatch to ensure above onUidStateChanged() job was digested.
+ final CountDownLatch latch = new CountDownLatch(1);
FgThread.getHandler().post(() -> {
- synchronized (sync) {
- sync.notify();
- }
+ latch.countDown();
});
- synchronized (sync) {
- sync.wait();
- }
+ latch.await();
+
assumeFalse(a.updateHintAllowed());
a.reportActualWorkDuration(DURATIONS_THREE, TIMESTAMPS_THREE);
verify(mNativeWrapperMock, never()).halReportActualWorkDuration(anyLong(), any(), any());
@@ -287,15 +287,14 @@ public class HintManagerServiceTest {
service.mUidObserver.onUidStateChanged(
a.mUid, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
- final Object sync = new Object();
+
+ // Using CountDownLatch to ensure above onUidStateChanged() job was digested.
+ final CountDownLatch latch = new CountDownLatch(1);
FgThread.getHandler().post(() -> {
- synchronized (sync) {
- sync.notify();
- }
+ latch.countDown();
});
- synchronized (sync) {
- sync.wait();
- }
+ latch.await();
+
assertFalse(a.updateHintAllowed());
}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
index dca67d6da7ad..76b6a820e4a7 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
@@ -17,6 +17,8 @@
package com.android.server.power.stats.wakeups;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SENSOR;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
@@ -50,6 +52,8 @@ public class CpuWakeupStatsTest {
private static final String KERNEL_REASON_ALARM_IRQ = "120 test.alarm.device";
private static final String KERNEL_REASON_WIFI_IRQ = "130 test.wifi.device";
private static final String KERNEL_REASON_SOUND_TRIGGER_IRQ = "129 test.sound_trigger.device";
+ private static final String KERNEL_REASON_SENSOR_IRQ = "15 test.sensor.device";
+ private static final String KERNEL_REASON_CELLULAR_DATA_IRQ = "18 test.cellular_data.device";
private static final String KERNEL_REASON_UNKNOWN_IRQ = "140 test.unknown.device";
private static final String KERNEL_REASON_UNKNOWN = "free-form-reason test.alarm.device";
private static final String KERNEL_REASON_ALARM_ABNORMAL = "-1 test.alarm.device";
@@ -110,6 +114,83 @@ public class CpuWakeupStatsTest {
}
@Test
+ public void irqAttributionAllCombinations() {
+ final int[] subsystems = new int[] {
+ CPU_WAKEUP_SUBSYSTEM_ALARM,
+ CPU_WAKEUP_SUBSYSTEM_WIFI,
+ CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
+ CPU_WAKEUP_SUBSYSTEM_SENSOR,
+ CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA,
+ };
+
+ final String[] kernelReasons = new String[] {
+ KERNEL_REASON_ALARM_IRQ,
+ KERNEL_REASON_WIFI_IRQ,
+ KERNEL_REASON_SOUND_TRIGGER_IRQ,
+ KERNEL_REASON_SENSOR_IRQ,
+ KERNEL_REASON_CELLULAR_DATA_IRQ,
+ };
+
+ final int[] uids = new int[] {
+ TEST_UID_2, TEST_UID_3, TEST_UID_4, TEST_UID_1, TEST_UID_5
+ };
+
+ final int[] procStates = new int[] {
+ TEST_PROC_STATE_2,
+ TEST_PROC_STATE_3,
+ TEST_PROC_STATE_4,
+ TEST_PROC_STATE_1,
+ TEST_PROC_STATE_5
+ };
+
+ final int total = subsystems.length;
+ assertThat(kernelReasons.length).isEqualTo(total);
+ assertThat(uids.length).isEqualTo(total);
+ assertThat(procStates.length).isEqualTo(total);
+
+ for (int mask = 1; mask < (1 << total); mask++) {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3,
+ mHandler);
+ populateDefaultProcStates(obj);
+
+ final long wakeupTime = mRandom.nextLong(123456);
+
+ String combinedKernelReason = null;
+ for (int i = 0; i < total; i++) {
+ if ((mask & (1 << i)) != 0) {
+ combinedKernelReason = (combinedKernelReason == null)
+ ? kernelReasons[i]
+ : String.join(":", combinedKernelReason, kernelReasons[i]);
+ }
+
+ obj.noteWakingActivity(subsystems[i], wakeupTime + 2, uids[i]);
+ }
+ obj.noteWakeupTimeAndReason(wakeupTime, 1, combinedKernelReason);
+
+ assertThat(obj.mWakeupAttribution.size()).isEqualTo(1);
+
+ final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ assertThat(attribution.size()).isEqualTo(Integer.bitCount(mask));
+
+ for (int i = 0; i < total; i++) {
+ if ((mask & (1 << i)) == 0) {
+ assertThat(attribution.contains(subsystems[i])).isFalse();
+ continue;
+ }
+ assertThat(attribution.contains(subsystems[i])).isTrue();
+ assertThat(attribution.get(subsystems[i]).get(uids[i])).isEqualTo(procStates[i]);
+
+ for (int j = 0; j < total; j++) {
+ if (i == j) {
+ continue;
+ }
+ assertThat(attribution.get(subsystems[i]).indexOfKey(uids[j])).isLessThan(0);
+ }
+ }
+ }
+ }
+
+ @Test
public void alarmIrqAttributionSolo() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 12423121;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 8fcbf2f9e97a..541739d50024 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -96,6 +96,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
public class ManagedServicesTest extends UiServiceTestCase {
@@ -1920,6 +1921,18 @@ public class ManagedServicesTest extends UiServiceTestCase {
assertTrue(service.isBound(cn_disallowed, 0));
}
+ @Test
+ public void isComponentEnabledForCurrentProfiles_isThreadSafe() throws InterruptedException {
+ for (UserInfo userInfo : mUm.getUsers()) {
+ mService.addApprovedList("pkg1/cmp1:pkg2/cmp2:pkg3/cmp3", userInfo.id, true);
+ }
+ testThreadSafety(() -> {
+ mService.rebindServices(false, 0);
+ assertThat(mService.isComponentEnabledForCurrentProfiles(
+ new ComponentName("pkg1", "cmp1"))).isTrue();
+ }, 20, 30);
+ }
+
private void mockServiceInfoWithMetaData(List<ComponentName> componentNames,
ManagedServices service, ArrayMap<ComponentName, Bundle> metaDatas)
throws RemoteException {
@@ -2298,4 +2311,38 @@ public class ManagedServicesTest extends UiServiceTestCase {
return false;
}
}
+
+ /**
+ * Helper method to test the thread safety of some operations.
+ *
+ * <p>Runs the supplied {@code operationToTest}, {@code nRunsPerThread} times,
+ * concurrently using {@code nThreads} threads, and waits for all of them to finish.
+ */
+ private static void testThreadSafety(Runnable operationToTest, int nThreads,
+ int nRunsPerThread) throws InterruptedException {
+ final CountDownLatch startLatch = new CountDownLatch(1);
+ final CountDownLatch doneLatch = new CountDownLatch(nThreads);
+
+ for (int i = 0; i < nThreads; i++) {
+ Runnable threadRunnable = () -> {
+ try {
+ startLatch.await();
+ for (int j = 0; j < nRunsPerThread; j++) {
+ operationToTest.run();
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ doneLatch.countDown();
+ }
+ };
+ new Thread(threadRunnable, "Test Thread #" + i).start();
+ }
+
+ // Ready set go
+ startLatch.countDown();
+
+ // Wait for all test threads to be done.
+ doneLatch.await();
+ }
}
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 dd9f3cb3d343..5dbc6ab4a6d0 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -40,7 +40,6 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
@@ -358,7 +357,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private static final int NOTIFICATION_LOCATION_UNKNOWN = 0;
private static final String VALID_CONVO_SHORTCUT_ID = "shortcut";
-
+ private static final String SEARCH_SELECTOR_PKG = "searchSelector";
@Mock
private NotificationListeners mListeners;
@Mock
@@ -549,6 +548,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// apps allowed as convos
mService.setStringArrayResourceValue(PKG_O);
+ TestableResources tr = mContext.getOrCreateTestableResources();
+ tr.addOverride(com.android.internal.R.string.config_defaultSearchSelectorPackageName,
+ SEARCH_SELECTOR_PKG);
+
mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
@@ -5308,7 +5311,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
- restored.populateFromXmlForRestore(parser, getContext());
+ restored.populateFromXmlForRestore(parser, true, getContext());
assertNull(restored.getSound());
}
@@ -10636,6 +10639,34 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void fixSystemNotification_defaultSearchSelectior_withOnGoingFlag_nondismissible()
+ throws Exception {
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = SEARCH_SELECTOR_PKG;
+ ai.uid = mUid;
+ ai.flags |= ApplicationInfo.FLAG_SYSTEM;
+
+ when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(ai);
+ when(mAppOpsManager.checkOpNoThrow(
+ AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, ai.uid,
+ ai.packageName)).thenReturn(AppOpsManager.MODE_IGNORED);
+ // Given: a notification from an app on the system partition has the flag
+ // FLAG_ONGOING_EVENT set
+ // feature flag: ALLOW_DISMISS_ONGOING is on
+ mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
+ Notification n = new Notification.Builder(mContext, "test")
+ .setOngoing(true)
+ .build();
+
+ // When: fix the notification with NotificationManagerService
+ mService.fixNotification(n, PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE, true);
+
+ // Then: the notification's flag FLAG_NO_DISMISS should be set
+ assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
+ }
+
+ @Test
public void fixCallNotification_withOnGoingFlag_shouldNotBeNonDismissible()
throws Exception {
// Given: a call notification has the flag FLAG_ONGOING_EVENT set
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 f6d10b9f371c..c78b03e4b5cb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -140,6 +140,7 @@ import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
@@ -170,6 +171,12 @@ public class PreferencesHelperTest extends UiServiceTestCase {
Uri.parse("content://" + TEST_AUTHORITY
+ "/internal/audio/media/10?title=Test&canonical=1");
+ private static final Uri ANDROID_RES_SOUND_URI =
+ Uri.parse("android.resource://" + TEST_AUTHORITY + "/raw/test");
+
+ private static final Uri FILE_SOUND_URI =
+ Uri.parse("file://" + TEST_AUTHORITY + "/product/media/test.ogg");
+
@Mock PermissionHelper mPermissionHelper;
@Mock RankingHandler mHandler;
@Mock PackageManager mPm;
@@ -1338,6 +1345,57 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
}
+ /**
+ * Test sound Uri restore retry behavior when channel is restored before package
+ * and then package is installed.
+ */
+ @Test
+ public void testRestoreXml_withNonExistentCanonicalizedSoundUriAndMissingPackage()
+ throws Exception {
+ // canonicalization returns CANONICAL_SOUND_URI for getSoundForBackup (backup part)
+ doReturn(CANONICAL_SOUND_URI)
+ .when(mTestIContentProvider).canonicalize(any(), eq(SOUND_URI));
+
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(SOUND_URI, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true,
+ USER_SYSTEM, channel.getId());
+
+ // canonicalization / uncanonicalization returns null for the restore part
+ doReturn(null)
+ .when(mTestIContentProvider).canonicalize(any(), eq(CANONICAL_SOUND_URI));
+ doReturn(null)
+ .when(mTestIContentProvider).uncanonicalize(any(), any());
+
+ // simulate package not installed
+ when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UNKNOWN_UID);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException());
+
+ loadStreamXml(baos, true, USER_SYSTEM);
+
+ // 1st restore pass fails
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(
+ PKG_N_MR1, UNKNOWN_UID, channel.getId(), false);
+ // sound is CANONICAL_SOUND_URI, unchanged from backup
+ assertEquals(CANONICAL_SOUND_URI, actualChannel.getSound());
+ // sound is flagged as not restored
+ assertFalse(actualChannel.isSoundRestored());
+
+ // package is "installed"
+ when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UID_N_MR1);
+
+ // Trigger 2nd restore pass
+ mHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_N_MR1},
+ new int[]{UID_N_MR1});
+
+ // sound is flagged as restored and set to default URI
+ assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
+ assertTrue(actualChannel.isSoundRestored());
+ }
+
/**
* Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to
@@ -1363,7 +1421,9 @@ public class PreferencesHelperTest extends UiServiceTestCase {
backupWithUncanonicalizedSoundUri.getBytes(), true, USER_SYSTEM);
NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, id, false);
+
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
+ assertTrue(actualChannel.isSoundRestored());
}
@Test
@@ -1389,6 +1449,73 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testBackupRestoreXml_withAndroidResourceSoundUri() throws Exception {
+ // Mock ContentResolver.getResourceId:
+ // throw exception on restore 1st pass => simulate app not installed yet
+ // then return a valid resource on package update => sim. app installed
+ ContentResolver contentResolver = mock(ContentResolver.class);
+ when(mContext.getContentResolver()).thenReturn(contentResolver);
+ ContentResolver.OpenResourceIdResult resId = mock(
+ ContentResolver.OpenResourceIdResult.class);
+ when(contentResolver.getResourceId(ANDROID_RES_SOUND_URI)).thenReturn(resId).thenThrow(
+ new FileNotFoundException("")).thenReturn(resId);
+
+ mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
+ mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(ANDROID_RES_SOUND_URI, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true,
+ USER_SYSTEM, channel.getId());
+
+ // simulate package not installed
+ when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UNKNOWN_UID);
+ when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenThrow(
+ new PackageManager.NameNotFoundException());
+
+ loadStreamXml(baos, true, USER_SYSTEM);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(
+ PKG_N_MR1, UNKNOWN_UID, channel.getId(), false);
+ // sound is ANDROID_RES_SOUND_URI, unchanged from backup
+ assertEquals(ANDROID_RES_SOUND_URI, actualChannel.getSound());
+ // sound is flagged as not restored
+ assertFalse(actualChannel.isSoundRestored());
+
+ // package is "installed"
+ when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UID_N_MR1);
+
+ // Trigger 2nd restore pass
+ mHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_N_MR1},
+ new int[]{UID_N_MR1});
+
+ // sound is flagged as restored
+ assertEquals(ANDROID_RES_SOUND_URI, actualChannel.getSound());
+ assertTrue(actualChannel.isSoundRestored());
+ }
+
+ @Test
+ public void testBackupRestoreXml_withFileResourceSoundUri() throws Exception {
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(FILE_SOUND_URI, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true,
+ USER_SYSTEM, channel.getId());
+
+ loadStreamXml(baos, true, USER_SYSTEM);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(
+ PKG_N_MR1, UID_N_MR1, channel.getId(), false);
+ // sound is FILE_SOUND_URI, unchanged from backup
+ assertEquals(FILE_SOUND_URI, actualChannel.getSound());
+ // sound is flagged as restored
+ assertTrue(actualChannel.isSoundRestored());
+ }
+
+ @Test
public void testChannelXml_backup() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index f12b53acd06b..fe7cd4a5edd9 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -89,6 +89,12 @@
<activity android:name="com.android.server.wm.SurfaceControlViewHostTests$TestActivity" />
+ <activity android:name="android.server.wm.scvh.SurfaceSyncGroupActivity"
+ android:screenOrientation="locked"
+ android:turnScreenOn="true"
+ android:theme="@style/WhiteBackgroundTheme"
+ android:exported="true"/>
+
<service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
android:foregroundServiceType="mediaProjection"
android:enabled="true">
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTests.java
new file mode 100644
index 000000000000..9db647acebb7
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTests.java
@@ -0,0 +1,187 @@
+/*
+ * 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.server.wm;
+
+import static android.window.SurfaceSyncGroup.TRANSACTION_READY_TIMEOUT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.platform.test.annotations.Presubmit;
+import android.server.wm.scvh.SurfaceSyncGroupActivity;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.view.cts.surfacevalidator.BitmapPixelChecker;
+import android.window.SurfaceSyncGroup;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@Presubmit
+public class SurfaceSyncGroupTests {
+ private static final String TAG = "SurfaceSyncGroupTests";
+
+ @Rule
+ public ActivityTestRule<SurfaceSyncGroupActivity> mActivityRule = new ActivityTestRule<>(
+ SurfaceSyncGroupActivity.class);
+
+ private SurfaceSyncGroupActivity mActivity;
+
+ Instrumentation mInstrumentation;
+
+ private final HandlerThread mHandlerThread = new HandlerThread("applyTransaction");
+ private Handler mHandler;
+
+ @Before
+ public void setup() {
+ mActivity = mActivityRule.getActivity();
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mHandlerThread.start();
+ mHandler = mHandlerThread.getThreadHandler();
+ }
+
+ @Test
+ public void testOverlappingSyncsEnsureOrder_WhenTimeout() throws InterruptedException {
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.format = PixelFormat.TRANSLUCENT;
+
+ CountDownLatch secondDrawCompleteLatch = new CountDownLatch(1);
+ CountDownLatch bothSyncGroupsComplete = new CountDownLatch(2);
+ final SurfaceSyncGroup firstSsg = new SurfaceSyncGroup(TAG + "-first");
+ final SurfaceSyncGroup secondSsg = new SurfaceSyncGroup(TAG + "-second");
+ final SurfaceSyncGroup infiniteSsg = new SurfaceSyncGroup(TAG + "-infinite");
+
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown);
+ firstSsg.addTransaction(t);
+
+ View backgroundView = mActivity.getBackgroundView();
+ firstSsg.add(backgroundView.getRootSurfaceControl(),
+ () -> mActivity.runOnUiThread(() -> backgroundView.setBackgroundColor(Color.RED)));
+
+ addSecondSyncGroup(secondSsg, secondDrawCompleteLatch, bothSyncGroupsComplete);
+
+ assertTrue("Failed to draw two frames",
+ secondDrawCompleteLatch.await(5, TimeUnit.SECONDS));
+
+ mHandler.postDelayed(() -> {
+ // Don't add a markSyncReady for the first sync group until after it's added to another
+ // SSG to ensure the timeout is longer than the second frame's timeout. The infinite SSG
+ // will never complete to ensure it reaches the timeout, but only after the second SSG
+ // had a chance to reach its timeout.
+ infiniteSsg.add(firstSsg, null /* runnable */);
+ firstSsg.markSyncReady();
+ }, 200);
+
+ assertTrue("Failed to wait for both SurfaceSyncGroups to apply",
+ bothSyncGroupsComplete.await(5, TimeUnit.SECONDS));
+
+ validateScreenshot();
+ }
+
+ @Test
+ public void testOverlappingSyncsEnsureOrder_WhileHoldingTransaction()
+ throws InterruptedException {
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.format = PixelFormat.TRANSLUCENT;
+
+ CountDownLatch secondDrawCompleteLatch = new CountDownLatch(1);
+ CountDownLatch bothSyncGroupsComplete = new CountDownLatch(2);
+
+ final SurfaceSyncGroup firstSsg = new SurfaceSyncGroup(TAG + "-first",
+ transaction -> mHandler.postDelayed(() -> {
+ try {
+ assertTrue("Failed to draw two frames",
+ secondDrawCompleteLatch.await(5, TimeUnit.SECONDS));
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ transaction.apply();
+ }, TRANSACTION_READY_TIMEOUT + 200));
+ final SurfaceSyncGroup secondSsg = new SurfaceSyncGroup(TAG + "-second");
+
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown);
+ firstSsg.addTransaction(t);
+
+ View backgroundView = mActivity.getBackgroundView();
+ firstSsg.add(backgroundView.getRootSurfaceControl(),
+ () -> mActivity.runOnUiThread(() -> backgroundView.setBackgroundColor(Color.RED)));
+ firstSsg.markSyncReady();
+
+ addSecondSyncGroup(secondSsg, secondDrawCompleteLatch, bothSyncGroupsComplete);
+
+ assertTrue("Failed to wait for both SurfaceSyncGroups to apply",
+ bothSyncGroupsComplete.await(5, TimeUnit.SECONDS));
+
+ validateScreenshot();
+ }
+
+ private void addSecondSyncGroup(SurfaceSyncGroup surfaceSyncGroup,
+ CountDownLatch waitForSecondDraw, CountDownLatch bothSyncGroupsComplete) {
+ View backgroundView = mActivity.getBackgroundView();
+ ViewTreeObserver viewTreeObserver = backgroundView.getViewTreeObserver();
+ viewTreeObserver.registerFrameCommitCallback(() -> mHandler.post(() -> {
+ surfaceSyncGroup.add(backgroundView.getRootSurfaceControl(),
+ () -> mActivity.runOnUiThread(
+ () -> backgroundView.setBackgroundColor(Color.BLUE)));
+
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown);
+ surfaceSyncGroup.addTransaction(t);
+ surfaceSyncGroup.markSyncReady();
+ viewTreeObserver.registerFrameCommitCallback(waitForSecondDraw::countDown);
+ }));
+ }
+
+ private void validateScreenshot() {
+ Bitmap screenshot = mInstrumentation.getUiAutomation().takeScreenshot(
+ mActivity.getWindow());
+ assertNotNull("Failed to generate a screenshot", screenshot);
+ Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
+ screenshot.recycle();
+
+ BitmapPixelChecker pixelChecker = new BitmapPixelChecker(Color.BLUE);
+ int halfWidth = swBitmap.getWidth() / 2;
+ int halfHeight = swBitmap.getHeight() / 2;
+ // We don't need to check all the pixels since we only care that at least some of them are
+ // blue. If the buffers were submitted out of order, all the pixels will be red.
+ Rect bounds = new Rect(halfWidth, halfHeight, halfWidth + 10, halfHeight + 10);
+ int numMatchingPixels = pixelChecker.getNumMatchingPixels(swBitmap, bounds);
+ assertEquals("Expected 100 received " + numMatchingPixels + " matching pixels", 100,
+ numMatchingPixels);
+
+ swBitmap.recycle();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 43b429c76749..653b52b06720 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -109,14 +109,19 @@ public class TransitionTests extends WindowTestsBase {
final SurfaceControl.Transaction mMockT = mock(SurfaceControl.Transaction.class);
private BLASTSyncEngine mSyncEngine;
+ private Transition createTestTransition(int transitType, TransitionController controller) {
+ return new Transition(transitType, 0 /* flags */, controller, controller.mSyncEngine);
+ }
+
private Transition createTestTransition(int transitType) {
final TransitionController controller = new TestTransitionController(
mock(ActivityTaskManagerService.class));
mSyncEngine = createTestBLASTSyncEngine();
- final Transition t = new Transition(transitType, 0 /* flags */, controller, mSyncEngine);
- t.startCollecting(0 /* timeoutMs */);
- return t;
+ controller.setSyncEngine(mSyncEngine);
+ final Transition out = createTestTransition(transitType, controller);
+ out.startCollecting(0 /* timeoutMs */);
+ return out;
}
@Test
@@ -367,7 +372,6 @@ public class TransitionTests extends WindowTestsBase {
final ActivityRecord act = createActivityRecord(tasks[i]);
// alternate so that the transition doesn't get promoted to the display area
act.setVisibleRequested((i % 2) == 0); // starts invisible
- act.visibleIgnoringKeyguard = (i % 2) == 0;
if (i == showWallpaperTask) {
doReturn(true).when(act).showWallpaper();
}
@@ -754,10 +758,8 @@ public class TransitionTests extends WindowTestsBase {
final ActivityRecord closing = createActivityRecord(oldTask);
closing.setOccludesParent(true);
- closing.visibleIgnoringKeyguard = true;
final ActivityRecord opening = createActivityRecord(newTask);
opening.setOccludesParent(true);
- opening.visibleIgnoringKeyguard = true;
// Start states.
changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, false /* exChg */));
@@ -795,10 +797,8 @@ public class TransitionTests extends WindowTestsBase {
final ActivityRecord closing = createActivityRecord(oldTask);
closing.setOccludesParent(true);
- closing.visibleIgnoringKeyguard = true;
final ActivityRecord opening = createActivityRecord(newTask);
opening.setOccludesParent(false);
- opening.visibleIgnoringKeyguard = true;
// Start states.
changes.put(newTask, new Transition.ChangeInfo(newTask, false /* vis */, true /* exChg */));
changes.put(oldTask, new Transition.ChangeInfo(oldTask, true /* vis */, false /* exChg */));
@@ -837,10 +837,8 @@ public class TransitionTests extends WindowTestsBase {
final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
closing.setOccludesParent(true);
- closing.visibleIgnoringKeyguard = true;
final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
opening.setOccludesParent(true);
- opening.visibleIgnoringKeyguard = true;
// Start states.
changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
false /* vis */, true /* exChg */));
@@ -881,10 +879,8 @@ public class TransitionTests extends WindowTestsBase {
final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
closing.setOccludesParent(true);
- closing.visibleIgnoringKeyguard = true;
final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
opening.setOccludesParent(false);
- opening.visibleIgnoringKeyguard = true;
// Start states.
changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
false /* vis */, true /* exChg */));
@@ -925,10 +921,8 @@ public class TransitionTests extends WindowTestsBase {
final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
opening.setOccludesParent(true);
- opening.visibleIgnoringKeyguard = true;
final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
closing.setOccludesParent(true);
- closing.visibleIgnoringKeyguard = true;
closing.finishing = true;
// Start states.
changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
@@ -970,10 +964,8 @@ public class TransitionTests extends WindowTestsBase {
final ActivityRecord opening = openingTaskFragment.getTopMostActivity();
opening.setOccludesParent(true);
- opening.visibleIgnoringKeyguard = true;
final ActivityRecord closing = closingTaskFragment.getTopMostActivity();
closing.setOccludesParent(false);
- closing.visibleIgnoringKeyguard = true;
closing.finishing = true;
// Start states.
changes.put(openingTaskFragment, new Transition.ChangeInfo(openingTaskFragment,
@@ -1282,6 +1274,7 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testIntermediateVisibility() {
final TransitionController controller = new TestTransitionController(mAtm);
+ controller.setSyncEngine(mWm.mSyncEngine);
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* playerProc */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
@@ -1365,6 +1358,7 @@ public class TransitionTests extends WindowTestsBase {
super.dispatchLegacyAppTransitionFinished(ar);
}
};
+ controller.setSyncEngine(mWm.mSyncEngine);
controller.mSnapshotController = mWm.mSnapshotController;
final TaskSnapshotController taskSnapshotController = controller.mSnapshotController
.mTaskSnapshotController;
@@ -1462,6 +1456,7 @@ public class TransitionTests extends WindowTestsBase {
@Test
public void testNotReadyPushPop() {
final TransitionController controller = new TestTransitionController(mAtm);
+ controller.setSyncEngine(mWm.mSyncEngine);
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player, null /* playerProc */);
final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
@@ -1918,6 +1913,110 @@ public class TransitionTests extends WindowTestsBase {
assertEquals(TRANSIT_CHANGE, info.getChanges().get(0).getMode());
}
+ @Test
+ public void testQueueStartCollect() {
+ final TransitionController controller = mAtm.getTransitionController();
+ final TestTransitionPlayer player = registerTestTransitionPlayer();
+
+ mSyncEngine = createTestBLASTSyncEngine();
+ controller.setSyncEngine(mSyncEngine);
+
+ final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
+ final Transition transitB = createTestTransition(TRANSIT_OPEN, controller);
+ final Transition transitC = createTestTransition(TRANSIT_OPEN, controller);
+
+ final boolean[] onStartA = new boolean[]{false, false};
+ final boolean[] onStartB = new boolean[]{false, false};
+ controller.startCollectOrQueue(transitA, (deferred) -> {
+ onStartA[0] = true;
+ onStartA[1] = deferred;
+ });
+ controller.startCollectOrQueue(transitB, (deferred) -> {
+ onStartB[0] = true;
+ onStartB[1] = deferred;
+ });
+ waitUntilHandlersIdle();
+
+ assertTrue(onStartA[0]);
+ assertFalse(onStartA[1]);
+ assertTrue(transitA.isCollecting());
+
+ // B should be queued, so no calls yet
+ assertFalse(onStartB[0]);
+ assertTrue(transitB.isPending());
+
+ // finish collecting A
+ transitA.start();
+ transitA.setAllReady();
+ mSyncEngine.tryFinishForTest(transitA.getSyncId());
+ waitUntilHandlersIdle();
+
+ assertTrue(transitA.isPlaying());
+ assertTrue(transitB.isCollecting());
+ assertTrue(onStartB[0]);
+ // Should receive deferred = true
+ assertTrue(onStartB[1]);
+
+ // finish collecting B
+ transitB.start();
+ transitB.setAllReady();
+ mSyncEngine.tryFinishForTest(transitB.getSyncId());
+ assertTrue(transitB.isPlaying());
+
+ // Now we should be able to start collecting directly a new transition
+ final boolean[] onStartC = new boolean[]{false, false};
+ controller.startCollectOrQueue(transitC, (deferred) -> {
+ onStartC[0] = true;
+ onStartC[1] = deferred;
+ });
+ waitUntilHandlersIdle();
+ assertTrue(onStartC[0]);
+ assertFalse(onStartC[1]);
+ assertTrue(transitC.isCollecting());
+ }
+
+ @Test
+ public void testQueueWithLegacy() {
+ final TransitionController controller = mAtm.getTransitionController();
+ final TestTransitionPlayer player = registerTestTransitionPlayer();
+
+ mSyncEngine = createTestBLASTSyncEngine();
+ controller.setSyncEngine(mSyncEngine);
+
+ final Transition transitA = createTestTransition(TRANSIT_OPEN, controller);
+ final Transition transitB = createTestTransition(TRANSIT_OPEN, controller);
+
+ controller.startCollectOrQueue(transitA, (deferred) -> {});
+
+ BLASTSyncEngine.SyncGroup legacySync = mSyncEngine.prepareSyncSet(
+ mock(BLASTSyncEngine.TransactionReadyListener.class), "test");
+ final boolean[] applyLegacy = new boolean[]{false};
+ controller.startLegacySyncOrQueue(legacySync, () -> applyLegacy[0] = true);
+ assertFalse(applyLegacy[0]);
+ waitUntilHandlersIdle();
+
+ controller.startCollectOrQueue(transitB, (deferred) -> {});
+ assertTrue(transitA.isCollecting());
+
+ // finish collecting A
+ transitA.start();
+ transitA.setAllReady();
+ mSyncEngine.tryFinishForTest(transitA.getSyncId());
+ waitUntilHandlersIdle();
+
+ assertTrue(transitA.isPlaying());
+ // legacy sync should start now
+ assertTrue(applyLegacy[0]);
+ // transitB must wait
+ assertTrue(transitB.isPending());
+
+ // finish legacy sync
+ mSyncEngine.setReady(legacySync.mSyncId);
+ mSyncEngine.tryFinishForTest(legacySync.mSyncId);
+ // transitioncontroller should be notified so it can start collecting B
+ assertTrue(transitB.isCollecting());
+ }
+
private static void makeTaskOrganized(Task... tasks) {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
for (Task t : tasks) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
index aadb38da5469..2e05e2073e80 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
@@ -81,7 +81,12 @@ final class VisualQueryDetectorSession extends DetectorSession {
void informRestartProcessLocked() {
Slog.v(TAG, "informRestartProcessLocked");
mUpdateStateAfterStartFinished.set(false);
- //TODO(b/261783819): Starts detection in VisualQueryDetectionService.
+ try {
+ mCallback.onProcessRestarted();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to communicate #onProcessRestarted", e);
+ notifyOnDetectorRemoteException();
+ }
}
void setVisualQueryDetectionAttentionListenerLocked(
diff --git a/telecomm/java/android/telecom/CallStreamingService.java b/telecomm/java/android/telecom/CallStreamingService.java
index 3f538a7f262d..df48cd6a16e2 100644
--- a/telecomm/java/android/telecom/CallStreamingService.java
+++ b/telecomm/java/android/telecom/CallStreamingService.java
@@ -52,6 +52,7 @@ import java.lang.annotation.RetentionPolicy;
* </service>
* }
* </pre>
+ *
* @hide
*/
@SystemApi
@@ -65,7 +66,7 @@ public abstract class CallStreamingService extends Service {
private static final int MSG_SET_STREAMING_CALL_ADAPTER = 1;
private static final int MSG_CALL_STREAMING_STARTED = 2;
private static final int MSG_CALL_STREAMING_STOPPED = 3;
- private static final int MSG_CALL_STREAMING_CHANGED_CHANGED = 4;
+ private static final int MSG_CALL_STREAMING_STATE_CHANGED = 4;
/** Default Handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -77,8 +78,10 @@ public abstract class CallStreamingService extends Service {
switch (msg.what) {
case MSG_SET_STREAMING_CALL_ADAPTER:
- mStreamingCallAdapter = new StreamingCallAdapter(
- (IStreamingCallAdapter) msg.obj);
+ if (msg.obj != null) {
+ mStreamingCallAdapter = new StreamingCallAdapter(
+ (IStreamingCallAdapter) msg.obj);
+ }
break;
case MSG_CALL_STREAMING_STARTED:
mCall = (StreamingCall) msg.obj;
@@ -90,10 +93,12 @@ public abstract class CallStreamingService extends Service {
mStreamingCallAdapter = null;
CallStreamingService.this.onCallStreamingStopped();
break;
- case MSG_CALL_STREAMING_CHANGED_CHANGED:
+ case MSG_CALL_STREAMING_STATE_CHANGED:
int state = (int) msg.obj;
- mCall.requestStreamingState(state);
- CallStreamingService.this.onCallStreamingStateChanged(state);
+ if (mStreamingCallAdapter != null) {
+ mCall.requestStreamingState(state);
+ CallStreamingService.this.onCallStreamingStateChanged(state);
+ }
break;
default:
break;
@@ -128,7 +133,7 @@ public abstract class CallStreamingService extends Service {
@Override
public void onCallStreamingStateChanged(int state) throws RemoteException {
- mHandler.obtainMessage(MSG_CALL_STREAMING_CHANGED_CHANGED, state).sendToTarget();
+ mHandler.obtainMessage(MSG_CALL_STREAMING_STATE_CHANGED, state).sendToTarget();
}
}
@@ -173,9 +178,12 @@ public abstract class CallStreamingService extends Service {
STREAMING_FAILED_ALREADY_STREAMING,
STREAMING_FAILED_NO_SENDER,
STREAMING_FAILED_SENDER_BINDING_ERROR
- })
+ })
@Retention(RetentionPolicy.SOURCE)
- public @interface StreamingFailedReason {};
+ public @interface StreamingFailedReason {
+ }
+
+ ;
/**
* Called when a {@code StreamingCall} has been added to this call streaming session. The call
diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java
index d676eeec4024..db38f8873a02 100644
--- a/telephony/java/android/telephony/AnomalyReporter.java
+++ b/telephony/java/android/telephony/AnomalyReporter.java
@@ -105,6 +105,8 @@ public final class AnomalyReporter {
* @param carrierId the carrier of the id associated with this event.
*/
public static void reportAnomaly(@NonNull UUID eventId, String description, int carrierId) {
+ Rlog.i(TAG, "reportAnomaly: Received anomaly event report with eventId= " + eventId
+ + " and description= " + description);
if (sContext == null) {
Rlog.w(TAG, "AnomalyReporter not yet initialized, dropping event=" + eventId);
return;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7abae1854025..f27c23b29d67 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -7640,6 +7640,25 @@ public class CarrierConfigManager {
public static final String KEY_EMERGENCY_SCAN_TIMER_SEC_INT =
KEY_PREFIX + "emergency_scan_timer_sec_int";
+ /**
+ * The timer to wait for the call completion on the cellular network before attempting the
+ * call over Wi-Fi. On timer expiry, if emergency call on Wi-Fi is allowed and possible,
+ * telephony shall cancel the scan on the cellular network and place the call on Wi-Fi.
+ * If dialing over cellular network is ongoing when timer expires, dialing over Wi-Fi
+ * will be requested only when the ongoing dialing fails. If emergency call on Wi-Fi is not
+ * possible, then domain selection continues to try dialing from the radio and the timer
+ * remains expired. Later when calling over Wi-Fi is possible and dialing over cellular
+ * networks fails, calling over Wi-Fi will be requested. The timer shall be restarted from
+ * initial state if calling over Wi-Fi fails.
+ * If this value is set to {@link #REDIAL_TIMER_DISABLED}, then the timer will never be
+ * started.
+ *
+ * The default value for the timer is {@link #REDIAL_TIMER_DISABLED}.
+ * @hide
+ */
+ public static final String KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT =
+ KEY_PREFIX + "maximum_cellular_search_timer_sec_int";
+
/** @hide */
@IntDef(prefix = "SCAN_TYPE_",
value = {
@@ -7734,10 +7753,12 @@ public class CarrierConfigManager {
KEY_PREFIX + "emergency_requires_volte_enabled_bool";
/**
- * This values indicates that the cross SIM redialing timer shall be disabled.
+ * This values indicates that the cross SIM redialing timer and maximum celluar search
+ * timer shall be disabled.
*
* @see #KEY_CROSS_STACK_REDIAL_TIMER_SEC_INT
* @see #KEY_QUICK_CROSS_STACK_REDIAL_TIMER_SEC_INT
+ * @see #KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT
* @hide
*/
public static final int REDIAL_TIMER_DISABLED = 0;
@@ -7841,6 +7862,7 @@ public class CarrierConfigManager {
defaults.putInt(KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT, VOWIFI_REQUIRES_NONE);
defaults.putInt(KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT, 1);
defaults.putInt(KEY_EMERGENCY_SCAN_TIMER_SEC_INT, 10);
+ defaults.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, REDIAL_TIMER_DISABLED);
defaults.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT, SCAN_TYPE_NO_PREFERENCE);
defaults.putInt(KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT, 0);
defaults.putBoolean(KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL, false);
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 19f2a9bc67a3..b7617099bae3 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -153,8 +153,9 @@ public final class TelephonyScanManager {
nsi = mScanInfo.get(message.arg2);
}
if (nsi == null) {
- throw new RuntimeException(
- "Failed to find NetworkScanInfo with id " + message.arg2);
+ Rlog.e(TAG, "Unexpceted message " + message.what
+ + " as there is no NetworkScanInfo with id " + message.arg2);
+ return;
}
final NetworkScanCallback callback = nsi.mCallback;
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index e5e8f0a41048..6c069d4ee843 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -166,7 +166,7 @@ public final class UiccCardInfo implements Parcelable {
+ " Please Use UiccPortInfo API instead");
}
//always return ICCID from first port.
- return getPorts().stream().findFirst().get().getIccId();
+ return mPortList.isEmpty() ? null : mPortList.get(0).getIccId();
}
/**
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 1863a03b58fe..dda73497e647 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -139,14 +139,16 @@ public class UiccSlotInfo implements Parcelable {
public UiccSlotInfo(boolean isEuicc, String cardId,
@CardStateInfo int cardStateInfo, boolean isExtendedApduSupported,
boolean isRemovable, @NonNull List<UiccPortInfo> portList) {
- this.mIsActive = portList.get(0).isActive();
this.mIsEuicc = isEuicc;
this.mCardId = cardId;
this.mCardStateInfo = cardStateInfo;
- this.mLogicalSlotIdx = portList.get(0).getLogicalSlotIndex();
this.mIsExtendedApduSupported = isExtendedApduSupported;
this.mIsRemovable = isRemovable;
this.mPortList = portList;
+ this.mIsActive = !portList.isEmpty() && portList.get(0).isActive();
+ this.mLogicalSlotIdx = portList.isEmpty()
+ ? SubscriptionManager.INVALID_PHONE_INDEX
+ : portList.get(0).getLogicalSlotIndex();
}
/**
@@ -164,8 +166,7 @@ public class UiccSlotInfo implements Parcelable {
throw new UnsupportedOperationException("getIsActive() is not supported by "
+ "UiccSlotInfo. Please Use UiccPortInfo API instead");
}
- //always return status from first port.
- return getPorts().stream().findFirst().get().isActive();
+ return mIsActive;
}
public boolean getIsEuicc() {
@@ -202,9 +203,7 @@ public class UiccSlotInfo implements Parcelable {
throw new UnsupportedOperationException("getLogicalSlotIdx() is not supported by "
+ "UiccSlotInfo. Please use UiccPortInfo API instead");
}
- //always return logical slot index from first port.
- //portList always have at least one element.
- return getPorts().stream().findFirst().get().getLogicalSlotIndex();
+ return mLogicalSlotIdx;
}
/**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index a72c12dcb463..c5a21a80d8d0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -250,7 +250,10 @@ open class PipAppHelper(instrumentation: Instrumentation) :
waitConditions = arrayOf(ConditionsFactory.hasPipWindow())
)
- wmHelper.StateSyncBuilder().withPipShown().waitForAndVerify()
+ wmHelper.StateSyncBuilder()
+ .withWindowSurfaceAppeared(this)
+ .withPipShown()
+ .waitForAndVerify()
}
/** Expand the PIP window back to full screen via intent and wait until the app is visible */