summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java75
-rw-r--r--core/api/current.txt6
-rw-r--r--core/api/system-current.txt24
-rw-r--r--core/api/test-current.txt11
-rw-r--r--core/java/android/app/Activity.java12
-rw-r--r--core/java/android/app/DisabledWallpaperManager.java31
-rw-r--r--core/java/android/app/StatusBarManager.java3
-rw-r--r--core/java/android/app/WallpaperManager.java2
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java17
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--core/java/android/appwidget/AppWidgetProviderInfo.java12
-rw-r--r--core/java/android/hardware/biometrics/BiometricOverlayConstants.java50
-rw-r--r--core/java/android/hardware/biometrics/SensorLocationInternal.aidl19
-rw-r--r--core/java/android/hardware/biometrics/SensorLocationInternal.java112
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java75
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManagerInternal.java28
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java9
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java83
-rw-r--r--core/java/android/hardware/fingerprint/ISidefpsController.aidl6
-rw-r--r--core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl9
-rw-r--r--core/java/android/provider/DeviceConfig.java7
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java2
-rw-r--r--core/java/android/view/InsetsVisibilities.java4
-rw-r--r--core/java/android/view/WindowManagerImpl.java4
-rw-r--r--core/java/android/window/ITaskOrganizer.aidl5
-rw-r--r--core/java/android/window/StartingWindowInfo.java15
-rw-r--r--core/java/android/window/TaskOrganizer.java9
-rw-r--r--core/java/android/window/WindowContainerTransaction.java34
-rw-r--r--core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java18
-rw-r--r--core/java/com/android/internal/app/chooser/SelectableTargetInfo.java2
-rw-r--r--core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java18
-rw-r--r--core/java/com/android/internal/policy/PhoneFallbackEventHandler.java8
-rw-r--r--core/java/com/android/internal/util/LatencyTracker.java150
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java9
-rw-r--r--core/res/AndroidManifest.xml1
-rw-r--r--core/res/res/drawable-nodpi/default_wallpaper.pngbin489912 -> 4958722 bytes
-rw-r--r--core/res/res/drawable-sw600dp-nodpi/default_wallpaper.pngbin1197339 -> 0 bytes
-rw-r--r--core/res/res/drawable-sw720dp-nodpi/default_wallpaper.pngbin1930496 -> 0 bytes
-rw-r--r--core/res/res/layout/accessibility_shortcut_chooser_item.xml17
-rw-r--r--core/res/res/layout/transient_notification.xml1
-rw-r--r--core/res/res/values/config.xml25
-rw-r--r--core/res/res/values/styles.xml1
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java18
-rw-r--r--data/etc/services.core.protolog.json6
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java10
-rw-r--r--libs/WindowManager/Shell/res/drawable/split_rounded_bottom.xml30
-rw-r--r--libs/WindowManager/Shell/res/drawable/split_rounded_left.xml30
-rw-r--r--libs/WindowManager/Shell/res/drawable/split_rounded_right.xml30
-rw-r--r--libs/WindowManager/Shell/res/drawable/split_rounded_top.xml26
-rw-r--r--libs/WindowManager/Shell/res/layout/split_divider.xml8
-rw-r--r--libs/WindowManager/Shell/res/values-land/styles.xml14
-rw-r--r--libs/WindowManager/Shell/res/values/styles.xml14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java91
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java109
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java160
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java87
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java22
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java82
-rw-r--r--media/java/android/media/AudioManager.java14
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl26
-rw-r--r--media/java/android/media/ISpatializerHeadToSoundStagePoseCallback.aidl31
-rw-r--r--media/java/android/media/ISpatializerHeadTrackingModeCallback.aidl (renamed from packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.java)28
-rw-r--r--media/java/android/media/Spatializer.java552
-rw-r--r--packages/SettingsLib/res/values-as/arrays.xml4
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml12
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-ko/arrays.xml2
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java12
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java10
-rw-r--r--packages/SystemUI/Android.bp2
-rw-r--r--packages/SystemUI/TEST_MAPPING28
-rw-r--r--packages/SystemUI/monet/Android.bp31
-rw-r--r--packages/SystemUI/monet/AndroidManifest.xml20
-rw-r--r--packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt246
-rw-r--r--packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java64
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml4
-rw-r--r--packages/SystemUI/res/drawable/logout_button_background.xml3
-rw-r--r--packages/SystemUI/res/layout/keyguard_qs_user_switch.xml32
-rw-r--r--packages/SystemUI/res/layout/sidefps_view.xml16
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml13
-rw-r--r--packages/SystemUI/res/layout/text_toast.xml1
-rw-r--r--packages/SystemUI/res/layout/udfps_view.xml2
-rw-r--r--packages/SystemUI/res/raw/sfps_pulse.json1
-rw-r--r--packages/SystemUI/res/raw/sfps_pulse_landscape.json1
-rw-r--r--packages/SystemUI/res/values-h800dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values/colors.xml3
-rw-r--r--packages/SystemUI/res/values/config.xml5
-rw-r--r--packages/SystemUI/res/values/dimens.xml15
-rw-r--r--packages/SystemUI/res/values/flags.xml2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt86
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt14
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt12
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt18
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt67
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java38
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java21
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java14
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconView.java17
-rw-r--r--packages/SystemUI/src/com/android/keyguard/LockIconViewController.java112
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/LatencyTester.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt148
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/OWNERS1
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java239
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt196
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java107
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java120
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLog.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogEvent.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt210
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt61
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java86
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java93
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt49
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt188
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java219
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt65
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java2
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java2
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java7
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java66
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java1
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java143
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java23
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java651
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java6
-rw-r--r--services/core/java/com/android/server/biometrics/Utils.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/EnrollClient.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java144
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/SidefpsHelper.java60
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java87
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java28
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java18
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java39
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java56
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java23
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java16
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java38
-rw-r--r--services/core/java/com/android/server/compat/CompatChange.java71
-rw-r--r--services/core/java/com/android/server/compat/CompatConfig.java387
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java19
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java60
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java57
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java7
-rw-r--r--services/core/java/com/android/server/vcn/Vcn.java14
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java6
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java22
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java51
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java46
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java9
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java85
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java15
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java39
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java4
-rw-r--r--services/core/java/com/android/server/wm/DragState.java3
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java18
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java4
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java8
-rw-r--r--services/core/java/com/android/server/wm/LocaleOverlayHelper.java62
-rw-r--r--services/core/java/com/android/server/wm/PackageConfigPersister.java67
-rw-r--r--services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java131
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java40
-rw-r--r--services/core/java/com/android/server/wm/Task.java45
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java20
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java64
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java13
-rw-r--r--services/core/java/com/android/server/wm/Transition.java17
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java30
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java33
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java30
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java32
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java7
-rw-r--r--services/core/jni/com_android_server_am_CachedAppOptimizer.cpp25
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp21
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java15
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java129
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java98
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java281
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java182
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java7
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java12
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java3
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java6
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java9
299 files changed, 8610 insertions, 2586 deletions
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index d4e32396187d..b49bbc5fca89 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -18,6 +18,7 @@ package com.android.server.appsearch;
import static android.app.appsearch.AppSearchResult.throwableToFailedResult;
import static android.os.Process.INVALID_UID;
+import android.Manifest;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.app.appsearch.AppSearchBatchResult;
@@ -331,6 +332,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -343,7 +345,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
@@ -422,6 +424,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -430,7 +433,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -457,6 +460,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -465,7 +469,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -495,6 +499,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -507,7 +512,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchBatchResult.Builder<String, Void> resultBuilder =
@@ -584,6 +589,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -596,7 +602,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
@@ -668,6 +674,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -680,7 +687,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
@@ -737,6 +744,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -749,7 +757,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
@@ -805,6 +813,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -813,7 +822,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -837,6 +846,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(packageName);
Objects.requireNonNull(userHandle);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -845,7 +855,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -874,6 +884,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -882,7 +893,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -929,6 +940,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -937,7 +949,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -994,6 +1006,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -1002,7 +1015,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -1044,6 +1057,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -1056,7 +1070,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchBatchResult.Builder<String, Void> resultBuilder =
@@ -1133,6 +1147,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -1145,7 +1160,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
@@ -1200,6 +1215,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
try {
@@ -1208,7 +1224,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
AppSearchUserInstance instance =
@@ -1233,6 +1249,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(userHandle);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -1245,7 +1262,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
@@ -1288,6 +1305,7 @@ public class AppSearchManagerService extends SystemService {
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
+ int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
EXECUTOR.execute(() -> {
@@ -1301,7 +1319,7 @@ public class AppSearchManagerService extends SystemService {
// Obtain the user where the client wants to run the operations in. This should
// end up being the same as userHandle, assuming it is not a special user and
// the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+ UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
verifyUserUnlocked(targetUser);
Context targetUserContext = mContext.createContextAsUser(targetUser,
@@ -1389,12 +1407,22 @@ public class AppSearchManagerService extends SystemService {
/**
* Helper for dealing with incoming user arguments to system service calls.
*
+ * <p>Takes care of checking permissions and if the target is special user, this method will
+ * simply throw.
+ *
* @param targetUserHandle The user which the caller is requesting to execute as.
+ * @param callingPid The actual pid of the caller as determined by Binder.
* @param callingUid The actual uid of the caller as determined by Binder.
+ *
* @return the user handle that the call should run as. Will always be a concrete user.
+ *
+ * @throws IllegalArgumentException if the target user is a special user.
+ * @throws SecurityException if caller trying to interact across user without
+ * {@link Manifest.permission#INTERACT_ACROSS_USERS_FULL}
*/
@NonNull
- private UserHandle handleIncomingUser(@NonNull UserHandle targetUserHandle, int callingUid) {
+ private UserHandle handleIncomingUser(@NonNull UserHandle targetUserHandle, int callingPid,
+ int callingUid) {
UserHandle callingUserHandle = UserHandle.getUserHandleForUid(callingUid);
if (callingUserHandle.equals(targetUserHandle)) {
return targetUserHandle;
@@ -1406,9 +1434,16 @@ public class AppSearchManagerService extends SystemService {
"Call does not support special user " + targetUserHandle);
}
+ if (mContext.checkPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ callingPid,
+ callingUid) == PackageManager.PERMISSION_GRANTED) {
+ return targetUserHandle;
+ }
throw new SecurityException(
- "Requested user, " + targetUserHandle + ", is not the same as the calling user, "
- + callingUserHandle + ".");
+ "Permission denied while calling from uid " + callingUid
+ + " with " + targetUserHandle + "; Requires permission: "
+ + Manifest.permission.INTERACT_ACROSS_USERS_FULL);
}
/**
diff --git a/core/api/current.txt b/core/api/current.txt
index d684e4b82bbe..ec8dce0837cd 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -20454,7 +20454,7 @@ package android.media {
method public String getProperty(String);
method public int getRingerMode();
method @Deprecated public int getRouting(int);
- method @Nullable public android.media.Spatializer getSpatializer();
+ method @NonNull public android.media.Spatializer getSpatializer();
method public int getStreamMaxVolume(int);
method public int getStreamMinVolume(int);
method public int getStreamVolume(int);
@@ -23854,9 +23854,13 @@ package android.media {
public class Spatializer {
method public void addOnSpatializerStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnSpatializerStateChangedListener);
method public boolean canBeSpatialized(@NonNull android.media.AudioAttributes, @NonNull android.media.AudioFormat);
+ method public int getImmersiveAudioLevel();
method public boolean isAvailable();
method public boolean isEnabled();
method public void removeOnSpatializerStateChangedListener(@NonNull android.media.Spatializer.OnSpatializerStateChangedListener);
+ field public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1; // 0x1
+ field public static final int SPATIALIZER_IMMERSIVE_LEVEL_NONE = 0; // 0x0
+ field public static final int SPATIALIZER_IMMERSIVE_LEVEL_OTHER = -1; // 0xffffffff
}
public static interface Spatializer.OnSpatializerStateChangedListener {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0d3dd7cb886e..9c01bcada1ee 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5375,9 +5375,33 @@ package android.media {
public class Spatializer {
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void addCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void addOnHeadTrackingModeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnHeadTrackingModeChangedListener);
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void clearOnHeadToSoundstagePoseUpdatedListener();
method @NonNull @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public java.util.List<android.media.AudioDeviceAttributes> getCompatibleAudioDevices();
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getDesiredHeadTrackingMode();
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getHeadTrackingMode();
+ method @NonNull @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public java.util.List<java.lang.Integer> getSupportedHeadTrackingModes();
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void recenterHeadTracker();
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void removeCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void removeOnHeadTrackingModeChangedListener(@NonNull android.media.Spatializer.OnHeadTrackingModeChangedListener);
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setDesiredHeadTrackingMode(int);
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setEnabled(boolean);
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setGlobalTransform(@NonNull float[]);
+ method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setOnHeadToSoundstagePoseUpdatedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnHeadToSoundstagePoseUpdatedListener);
+ field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_DISABLED = -1; // 0xffffffff
+ field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_OTHER = 0; // 0x0
+ field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_RELATIVE_DEVICE = 2; // 0x2
+ field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_RELATIVE_WORLD = 1; // 0x1
+ field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_UNSUPPORTED = -2; // 0xfffffffe
+ }
+
+ public static interface Spatializer.OnHeadToSoundstagePoseUpdatedListener {
+ method public void onHeadToSoundstagePoseUpdated(@NonNull android.media.Spatializer, @NonNull float[]);
+ }
+
+ public static interface Spatializer.OnHeadTrackingModeChangedListener {
+ method public void onDesiredHeadTrackingModeChanged(@NonNull android.media.Spatializer, int);
+ method public void onHeadTrackingModeChanged(@NonNull android.media.Spatializer, int);
}
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f7164cf3761e..2ecf088fb5d0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -425,6 +425,7 @@ package android.app.admin {
public class DevicePolicyManager {
method public int checkProvisioningPreCondition(@Nullable String, @NonNull String);
+ method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void clearOrganizationId();
method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord();
method @Nullable public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
@@ -3295,6 +3296,7 @@ package android.window {
method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int);
method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken);
+ method @NonNull public android.window.WindowContainerTransaction setAdjacentTaskFragments(@NonNull android.os.IBinder, @Nullable android.os.IBinder, @Nullable android.window.WindowContainerTransaction.TaskFragmentAdjacentParams);
method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setBoundsChangeTransaction(@NonNull android.window.WindowContainerToken, @NonNull android.view.SurfaceControl.Transaction);
@@ -3310,6 +3312,15 @@ package android.window {
field @NonNull public static final android.os.Parcelable.Creator<android.window.WindowContainerTransaction> CREATOR;
}
+ public static class WindowContainerTransaction.TaskFragmentAdjacentParams {
+ ctor public WindowContainerTransaction.TaskFragmentAdjacentParams();
+ ctor public WindowContainerTransaction.TaskFragmentAdjacentParams(@NonNull android.os.Bundle);
+ method public void setShouldDelayPrimaryLastActivityRemoval(boolean);
+ method public void setShouldDelaySecondaryLastActivityRemoval(boolean);
+ method public boolean shouldDelayPrimaryLastActivityRemoval();
+ method public boolean shouldDelaySecondaryLastActivityRemoval();
+ }
+
public abstract class WindowContainerTransactionCallback {
ctor public WindowContainerTransactionCallback();
method public abstract void onTransactionReady(int, @NonNull android.view.SurfaceControl.Transaction);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 643020a7dbd2..12025f98ada8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1934,10 +1934,14 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Called after {@link #onRestoreInstanceState}, {@link #onRestart}, or
- * {@link #onPause}, for your activity to start interacting with the user. This is an indicator
- * that the activity became active and ready to receive input. It is on top of an activity stack
- * and visible to user.
+ * Called after {@link #onRestoreInstanceState}, {@link #onRestart}, or {@link #onPause}. This
+ * is usually a hint for your activity to start interacting with the user, which is a good
+ * indicator that the activity became active and ready to receive input. This sometimes could
+ * also be a transit state toward another resting state. For instance, an activity may be
+ * relaunched to {@link #onPause} due to configuration changes and the activity was visible,
+ * but wasn’t the top-most activity of an activity task. {@link #onResume} is guaranteed to be
+ * called before {@link #onPause} in this case which honors the activity lifecycle policy and
+ * the activity eventually rests in {@link #onPause}.
*
* <p>On platform versions prior to {@link android.os.Build.VERSION_CODES#Q} this is also a good
* place to try to open exclusive-access devices or to get access to singleton resources.
diff --git a/core/java/android/app/DisabledWallpaperManager.java b/core/java/android/app/DisabledWallpaperManager.java
index 1f323c378093..ae3a9e6668ab 100644
--- a/core/java/android/app/DisabledWallpaperManager.java
+++ b/core/java/android/app/DisabledWallpaperManager.java
@@ -74,6 +74,11 @@ final class DisabledWallpaperManager extends WallpaperManager {
return false;
}
+ private static int unsupportedInt() {
+ if (DEBUG) Log.w(TAG, "unsupported method called; returning -1", new Exception());
+ return -1;
+ }
+
@Override
public Drawable getDrawable() {
return unsupported();
@@ -189,12 +194,12 @@ final class DisabledWallpaperManager extends WallpaperManager {
@Override
public int getWallpaperId(int which) {
- return unsupported();
+ return unsupportedInt();
}
@Override
public int getWallpaperIdForUser(int which, int userId) {
- return unsupported();
+ return unsupportedInt();
}
@Override
@@ -209,7 +214,8 @@ final class DisabledWallpaperManager extends WallpaperManager {
@Override
public int setResource(int resid, int which) throws IOException {
- return unsupported();
+ unsupported();
+ return 0;
}
@Override
@@ -220,19 +226,22 @@ final class DisabledWallpaperManager extends WallpaperManager {
@Override
public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup)
throws IOException {
- return unsupported();
+ unsupported();
+ return 0;
}
@Override
public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, int which)
throws IOException {
- return unsupported();
+ unsupported();
+ return 0;
}
@Override
public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup, int which,
int userId) throws IOException {
- return unsupported();
+ unsupported();
+ return 0;
}
@Override
@@ -243,13 +252,15 @@ final class DisabledWallpaperManager extends WallpaperManager {
@Override
public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup)
throws IOException {
- return unsupported();
+ unsupported();
+ return 0;
}
@Override
public int setStream(InputStream bitmapData, Rect visibleCropHint, boolean allowBackup,
int which) throws IOException {
- return unsupported();
+ unsupported();
+ return 0;
}
@Override
@@ -259,12 +270,12 @@ final class DisabledWallpaperManager extends WallpaperManager {
@Override
public int getDesiredMinimumWidth() {
- return unsupported();
+ return unsupportedInt();
}
@Override
public int getDesiredMinimumHeight() {
- return unsupported();
+ return unsupportedInt();
}
@Override
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 77bcef3ae009..be702c2a1756 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -46,7 +46,7 @@ import java.lang.annotation.RetentionPolicy;
*/
@SystemService(Context.STATUS_BAR_SERVICE)
public class StatusBarManager {
-
+ // LINT.IfChange
/** @hide */
public static final int DISABLE_EXPAND = View.STATUS_BAR_DISABLE_EXPAND;
/** @hide */
@@ -144,6 +144,7 @@ public class StatusBarManager {
})
@Retention(RetentionPolicy.SOURCE)
public @interface Disable2Flags {}
+ // LINT.ThenChange(frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt)
/**
* Default disable flags for setup
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 123046edd577..b570ae60ee91 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -562,7 +562,7 @@ public class WallpaperManager {
}
if (returnDefault) {
Bitmap defaultWallpaper = mDefaultWallpaper;
- if (defaultWallpaper == null) {
+ if (defaultWallpaper == null || defaultWallpaper.isRecycled()) {
defaultWallpaper = getDefaultWallpaper(context, which);
synchronized (this) {
mDefaultWallpaper = defaultWallpaper;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0e04ad3768c7..12444ab9ab09 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -13740,6 +13740,23 @@ public class DevicePolicyManager {
}
/**
+ * Clears organization ID set by the DPC and resets the precomputed enrollment specific ID.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public void clearOrganizationId() {
+ if (mService == null) {
+ return;
+ }
+ try {
+ mService.clearOrganizationIdForUser(myUserId());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates and provisions a managed profile and sets the
* {@link ManagedProfileProvisioningParams#getProfileAdminComponentName()} as the profile
* owner.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b6c48a1c057b..b1364b500d4a 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -377,6 +377,7 @@ interface IDevicePolicyManager {
void setOrganizationColor(in ComponentName admin, in int color);
void setOrganizationColorForUser(in int color, in int userId);
+ void clearOrganizationIdForUser(int userHandle);
int getOrganizationColor(in ComponentName admin);
int getOrganizationColorForUser(int userHandle);
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 063ba1174cdc..2e94dd1a47c4 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -143,7 +143,7 @@ public class AppWidgetProviderInfo implements Parcelable {
public ComponentName provider;
/**
- * The default height of the widget when added to a host, in dp. The widget will get
+ * The default height of the widget when added to a host, in px. The widget will get
* at least this width, and will often be given more, depending on the host.
*
* <p>This field corresponds to the <code>android:minWidth</code> attribute in
@@ -152,7 +152,7 @@ public class AppWidgetProviderInfo implements Parcelable {
public int minWidth;
/**
- * The default height of the widget when added to a host, in dp. The widget will get
+ * The default height of the widget when added to a host, in px. The widget will get
* at least this height, and will often be given more, depending on the host.
*
* <p>This field corresponds to the <code>android:minHeight</code> attribute in
@@ -161,7 +161,7 @@ public class AppWidgetProviderInfo implements Parcelable {
public int minHeight;
/**
- * Minimum width (in dp) which the widget can be resized to. This field has no effect if it
+ * Minimum width (in px) which the widget can be resized to. This field has no effect if it
* is greater than minWidth or if horizontal resizing isn't enabled (see {@link #resizeMode}).
*
* <p>This field corresponds to the <code>android:minResizeWidth</code> attribute in
@@ -170,7 +170,7 @@ public class AppWidgetProviderInfo implements Parcelable {
public int minResizeWidth;
/**
- * Minimum height (in dp) which the widget can be resized to. This field has no effect if it
+ * Minimum height (in px) which the widget can be resized to. This field has no effect if it
* is greater than minHeight or if vertical resizing isn't enabled (see {@link #resizeMode}).
*
* <p>This field corresponds to the <code>android:minResizeHeight</code> attribute in
@@ -179,7 +179,7 @@ public class AppWidgetProviderInfo implements Parcelable {
public int minResizeHeight;
/**
- * Maximum width (in dp) which the widget can be resized to. This field has no effect if it is
+ * Maximum width (in px) which the widget can be resized to. This field has no effect if it is
* smaller than minWidth or if horizontal resizing isn't enabled (see {@link #resizeMode}).
*
* <p>This field corresponds to the <code>android:maxResizeWidth</code> attribute in the
@@ -189,7 +189,7 @@ public class AppWidgetProviderInfo implements Parcelable {
public int maxResizeWidth;
/**
- * Maximum height (in dp) which the widget can be resized to. This field has no effect if it is
+ * Maximum height (in px) which the widget can be resized to. This field has no effect if it is
* smaller than minHeight or if vertical resizing isn't enabled (see {@link #resizeMode}).
*
* <p>This field corresponds to the <code>android:maxResizeHeight</code> attribute in the
diff --git a/core/java/android/hardware/biometrics/BiometricOverlayConstants.java b/core/java/android/hardware/biometrics/BiometricOverlayConstants.java
new file mode 100644
index 000000000000..603b06ddabaa
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricOverlayConstants.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Common constants for biometric overlays.
+ * @hide
+ */
+public interface BiometricOverlayConstants {
+ /** Unknown usage. */
+ int REASON_UNKNOWN = 0;
+ /** User is about to enroll. */
+ int REASON_ENROLL_FIND_SENSOR = 1;
+ /** User is enrolling. */
+ int REASON_ENROLL_ENROLLING = 2;
+ /** Usage from BiometricPrompt. */
+ int REASON_AUTH_BP = 3;
+ /** Usage from Keyguard. */
+ int REASON_AUTH_KEYGUARD = 4;
+ /** Non-specific usage (from FingerprintManager). */
+ int REASON_AUTH_OTHER = 5;
+
+ @IntDef({REASON_UNKNOWN,
+ REASON_ENROLL_FIND_SENSOR,
+ REASON_ENROLL_ENROLLING,
+ REASON_AUTH_BP,
+ REASON_AUTH_KEYGUARD,
+ REASON_AUTH_OTHER})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ShowReason {}
+}
diff --git a/core/java/android/hardware/biometrics/SensorLocationInternal.aidl b/core/java/android/hardware/biometrics/SensorLocationInternal.aidl
new file mode 100644
index 000000000000..098190449d53
--- /dev/null
+++ b/core/java/android/hardware/biometrics/SensorLocationInternal.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.biometrics;
+
+// @hide
+parcelable SensorLocationInternal;
diff --git a/core/java/android/hardware/biometrics/SensorLocationInternal.java b/core/java/android/hardware/biometrics/SensorLocationInternal.java
new file mode 100644
index 000000000000..fb25a2fcd823
--- /dev/null
+++ b/core/java/android/hardware/biometrics/SensorLocationInternal.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The location of a sensor relative to a physical display.
+ *
+ * Note that the location may change depending on other attributes of the device, such as
+ * fold status, which are not yet included in this class.
+ * @hide
+ */
+public class SensorLocationInternal implements Parcelable {
+
+ /** Default value to use when the sensor's location is unknown or undefined. */
+ public static final SensorLocationInternal DEFAULT = new SensorLocationInternal("", 0, 0, 0);
+
+ /**
+ * The stable display id.
+ */
+ @NonNull
+ public final String displayId;
+
+ /**
+ * The location of the center of the sensor if applicable. For example, sensors of type
+ * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
+ * distance in pixels, measured from the left edge of the screen.
+ */
+ public final int sensorLocationX;
+
+ /**
+ * The location of the center of the sensor if applicable. For example, sensors of type
+ * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
+ * distance in pixels, measured from the top edge of the screen.
+ *
+ */
+ public final int sensorLocationY;
+
+ /**
+ * The radius of the sensor if applicable. For example, sensors of type
+ * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the radius
+ * of the sensor, in pixels.
+ */
+ public final int sensorRadius;
+
+ public SensorLocationInternal(@Nullable String displayId,
+ int sensorLocationX, int sensorLocationY, int sensorRadius) {
+ this.displayId = displayId != null ? displayId : "";
+ this.sensorLocationX = sensorLocationX;
+ this.sensorLocationY = sensorLocationY;
+ this.sensorRadius = sensorRadius;
+ }
+
+ protected SensorLocationInternal(Parcel in) {
+ displayId = in.readString16NoHelper();
+ sensorLocationX = in.readInt();
+ sensorLocationY = in.readInt();
+ sensorRadius = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(displayId);
+ dest.writeInt(sensorLocationX);
+ dest.writeInt(sensorLocationY);
+ dest.writeInt(sensorRadius);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<SensorLocationInternal> CREATOR =
+ new Creator<SensorLocationInternal>() {
+ @Override
+ public SensorLocationInternal createFromParcel(Parcel in) {
+ return new SensorLocationInternal(in);
+ }
+
+ @Override
+ public SensorLocationInternal[] newArray(int size) {
+ return new SensorLocationInternal[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "[id: " + displayId
+ + ", x: " + sensorLocationX
+ + ", y: " + sensorLocationY
+ + ", r: " + sensorRadius + "]";
+ }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index fc728a22ed5a..4708f3e0664f 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -104,6 +104,9 @@ public class CameraDeviceImpl extends CameraDevice
private SparseArray<CaptureCallbackHolder> mCaptureCallbackMap =
new SparseArray<CaptureCallbackHolder>();
+ /** map request IDs which have batchedOutputs to requestCount*/
+ private HashMap<Integer, Integer> mBatchOutputMap = new HashMap<>();
+
private int mRepeatingRequestId = REQUEST_ID_NONE;
// Latest repeating request list's types
private int[] mRepeatingRequestTypes;
@@ -973,6 +976,7 @@ public class CameraDeviceImpl extends CameraDevice
mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(REQUEST_ID_NONE, null);
mIdle = true;
mCaptureCallbackMap = new SparseArray<CaptureCallbackHolder>();
+ mBatchOutputMap = new HashMap<>();
mFrameNumberTracker = new FrameNumberTracker();
mCurrentSession.closeWithoutDraining();
@@ -1179,6 +1183,41 @@ public class CameraDeviceImpl extends CameraDevice
return requestTypes;
}
+ private boolean hasBatchedOutputs(List<CaptureRequest> requestList) {
+ boolean hasBatchedOutputs = true;
+ for (int i = 0; i < requestList.size(); i++) {
+ CaptureRequest request = requestList.get(i);
+ if (!request.isPartOfCRequestList()) {
+ hasBatchedOutputs = false;
+ break;
+ }
+ if (i == 0) {
+ Collection<Surface> targets = request.getTargets();
+ if (targets.size() != 2) {
+ hasBatchedOutputs = false;
+ break;
+ }
+ }
+ }
+ return hasBatchedOutputs;
+ }
+
+ private void updateTracker(int requestId, long frameNumber,
+ int requestType, CaptureResult result, boolean isPartialResult) {
+ int requestCount = 1;
+ // If the request has batchedOutputs update each frame within the batch.
+ if (mBatchOutputMap.containsKey(requestId)) {
+ requestCount = mBatchOutputMap.get(requestId);
+ for (int i = 0; i < requestCount; i++) {
+ mFrameNumberTracker.updateTracker(frameNumber - (requestCount - 1 - i),
+ result, isPartialResult, requestType);
+ }
+ } else {
+ mFrameNumberTracker.updateTracker(frameNumber, result,
+ isPartialResult, requestType);
+ }
+ }
+
private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback,
Executor executor, boolean repeating) throws CameraAccessException {
@@ -1224,6 +1263,14 @@ public class CameraDeviceImpl extends CameraDevice
request.recoverStreamIdToSurface();
}
+ // If the request has batched outputs, then store the
+ // requestCount and requestId in the map.
+ boolean hasBatchedOutputs = hasBatchedOutputs(requestList);
+ if (hasBatchedOutputs) {
+ int requestCount = requestList.size();
+ mBatchOutputMap.put(requestInfo.getRequestId(), requestCount);
+ }
+
if (callback != null) {
mCaptureCallbackMap.put(requestInfo.getRequestId(),
new CaptureCallbackHolder(
@@ -1839,8 +1886,18 @@ public class CameraDeviceImpl extends CameraDevice
if (DEBUG) {
Log.v(TAG, String.format("got error frame %d", frameNumber));
}
- mFrameNumberTracker.updateTracker(frameNumber,
- /*error*/true, request.getRequestType());
+
+ // Update FrameNumberTracker for every frame during HFR mode.
+ if (mBatchOutputMap.containsKey(requestId)) {
+ for (int i = 0; i < mBatchOutputMap.get(requestId); i++) {
+ mFrameNumberTracker.updateTracker(frameNumber - (subsequenceId - i),
+ /*error*/true, request.getRequestType());
+ }
+ } else {
+ mFrameNumberTracker.updateTracker(frameNumber,
+ /*error*/true, request.getRequestType());
+ }
+
checkAndFireSequenceComplete();
// Dispatch the failure callback
@@ -2023,7 +2080,6 @@ public class CameraDeviceImpl extends CameraDevice
public void onResultReceived(CameraMetadataNative result,
CaptureResultExtras resultExtras, PhysicalCaptureResultInfo physicalResults[])
throws RemoteException {
-
int requestId = resultExtras.getRequestId();
long frameNumber = resultExtras.getFrameNumber();
@@ -2064,8 +2120,8 @@ public class CameraDeviceImpl extends CameraDevice
+ frameNumber);
}
- mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
- requestType);
+ updateTracker(requestId, frameNumber, requestType, /*result*/null,
+ isPartialResult);
return;
}
@@ -2077,8 +2133,9 @@ public class CameraDeviceImpl extends CameraDevice
+ frameNumber);
}
- mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
- requestType);
+ updateTracker(requestId, frameNumber, requestType, /*result*/null,
+ isPartialResult);
+
return;
}
@@ -2184,9 +2241,7 @@ public class CameraDeviceImpl extends CameraDevice
Binder.restoreCallingIdentity(ident);
}
- // Collect the partials for a total result; or mark the frame as totally completed
- mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult,
- requestType);
+ updateTracker(requestId, frameNumber, requestType, finalResult, isPartialResult);
// Fire onCaptureSequenceCompleted
if (!isPartialResult) {
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerInternal.java b/core/java/android/hardware/devicestate/DeviceStateManagerInternal.java
new file mode 100644
index 000000000000..4c91c160f891
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerInternal.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.devicestate;
+
+/**
+ * Device state manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class DeviceStateManagerInternal {
+
+ /** Returns the list of currently supported device state identifiers. */
+ public abstract int[] getSupportedStateIdentifiers();
+}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 4f205530ef0d..5bb51c19342d 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -35,6 +35,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* Display manager local system service interface.
@@ -129,6 +130,14 @@ public abstract class DisplayManagerInternal {
public abstract DisplayInfo getDisplayInfo(int displayId);
/**
+ * Returns a set of DisplayInfo, for the states that may be assumed by either the given display,
+ * or any other display within that display's group.
+ *
+ * @param displayId The logical display id to fetch DisplayInfo for.
+ */
+ public abstract Set<DisplayInfo> getPossibleDisplayInfo(int displayId);
+
+ /**
* Returns the position of the display's projection.
*
* @param displayId The logical display id.
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index 45c6b290be11..4bf9a740f971 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -21,7 +21,9 @@ import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFP
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.os.Parcel;
@@ -38,34 +40,14 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
*/
public final @FingerprintSensorProperties.SensorType int sensorType;
- /**
- * The location of the center of the sensor if applicable. For example, sensors of type
- * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
- * distance in pixels, measured from the left edge of the screen.
- */
- public final int sensorLocationX;
-
- /**
- * The location of the center of the sensor if applicable. For example, sensors of type
- * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the
- * distance in pixels, measured from the top edge of the screen.
- *
- */
- public final int sensorLocationY;
-
- /**
- * The radius of the sensor if applicable. For example, sensors of type
- * {@link FingerprintSensorProperties#TYPE_UDFPS_OPTICAL} would report this value as the radius
- * of the sensor, in pixels.
- */
- public final int sensorRadius;
+ private final List<SensorLocationInternal> mSensorLocations;
public FingerprintSensorPropertiesInternal(int sensorId,
@SensorProperties.Strength int strength, int maxEnrollmentsPerUser,
@NonNull List<ComponentInfoInternal> componentInfo,
@FingerprintSensorProperties.SensorType int sensorType,
- boolean resetLockoutRequiresHardwareAuthToken, int sensorLocationX, int sensorLocationY,
- int sensorRadius) {
+ boolean resetLockoutRequiresHardwareAuthToken,
+ @NonNull List<SensorLocationInternal> sensorLocations) {
// IBiometricsFingerprint@2.1 handles lockout in the framework, so the challenge is not
// required as it can only be generated/attested/verified by TEE components.
// IFingerprint@1.0 handles lockout below the HAL, but does not require a challenge. See
@@ -73,9 +55,7 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
super(sensorId, strength, maxEnrollmentsPerUser, componentInfo,
resetLockoutRequiresHardwareAuthToken, false /* resetLockoutRequiresChallenge */);
this.sensorType = sensorType;
- this.sensorLocationX = sensorLocationX;
- this.sensorLocationY = sensorLocationY;
- this.sensorRadius = sensorRadius;
+ this.mSensorLocations = List.copyOf(sensorLocations);
}
/**
@@ -88,16 +68,15 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
boolean resetLockoutRequiresHardwareAuthToken) {
// TODO(b/179175438): Value should be provided from the HAL
this(sensorId, strength, maxEnrollmentsPerUser, componentInfo, sensorType,
- resetLockoutRequiresHardwareAuthToken, 540 /* sensorLocationX */,
- 1636 /* sensorLocationY */, 130 /* sensorRadius */);
+ resetLockoutRequiresHardwareAuthToken, List.of(new SensorLocationInternal(
+ "" /* displayId */, 540 /* sensorLocationX */, 1636 /* sensorLocationY */,
+ 130 /* sensorRadius */)));
}
protected FingerprintSensorPropertiesInternal(Parcel in) {
super(in);
sensorType = in.readInt();
- sensorLocationX = in.readInt();
- sensorLocationY = in.readInt();
- sensorRadius = in.readInt();
+ mSensorLocations = in.createTypedArrayList(SensorLocationInternal.CREATOR);
}
public static final Creator<FingerprintSensorPropertiesInternal> CREATOR =
@@ -122,9 +101,7 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(sensorType);
- dest.writeInt(sensorLocationX);
- dest.writeInt(sensorLocationY);
- dest.writeInt(sensorRadius);
+ dest.writeTypedList(mSensorLocations);
}
public boolean isAnyUdfpsType() {
@@ -150,6 +127,44 @@ public class FingerprintSensorPropertiesInternal extends SensorPropertiesInterna
}
}
+ /**
+ * Get the default location.
+ *
+ * Use this method when the sensor's relationship to the displays on the device do not
+ * matter.
+ * @return
+ */
+ @NonNull
+ public SensorLocationInternal getLocation() {
+ final SensorLocationInternal location = getLocation("" /* displayId */);
+ return location != null ? location : SensorLocationInternal.DEFAULT;
+ }
+
+ /**
+ * Get the location of a sensor relative to a physical display layout.
+ *
+ * @param displayId stable display id
+ * @return location or null if none is specified
+ */
+ @Nullable
+ public SensorLocationInternal getLocation(String displayId) {
+ for (SensorLocationInternal location : mSensorLocations) {
+ if (location.displayId.equals(displayId)) {
+ return location;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Gets all locations relative to all supported display layouts.
+ * @return supported locations
+ */
+ @NonNull
+ public List<SensorLocationInternal> getAllLocations() {
+ return mSensorLocations;
+ }
+
@Override
public String toString() {
return "ID: " + sensorId + ", Strength: " + sensorStrength + ", Type: " + sensorType;
diff --git a/core/java/android/hardware/fingerprint/ISidefpsController.aidl b/core/java/android/hardware/fingerprint/ISidefpsController.aidl
index 00f40048cbae..684f18f3fd94 100644
--- a/core/java/android/hardware/fingerprint/ISidefpsController.aidl
+++ b/core/java/android/hardware/fingerprint/ISidefpsController.aidl
@@ -21,9 +21,9 @@ package android.hardware.fingerprint;
*/
oneway interface ISidefpsController {
- // Shows the overlay.
- void show();
+ // Shows the overlay for the given sensor with a reason from BiometricOverlayConstants.
+ void show(int sensorId, int reason);
// Hides the overlay.
- void hide();
+ void hide(int sensorId);
}
diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
index f18360ff4108..648edda62171 100644
--- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
+++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
@@ -22,14 +22,7 @@ import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
* @hide
*/
oneway interface IUdfpsOverlayController {
- const int REASON_UNKNOWN = 0;
- const int REASON_ENROLL_FIND_SENSOR = 1;
- const int REASON_ENROLL_ENROLLING = 2;
- const int REASON_AUTH_BP = 3; // BiometricPrompt
- const int REASON_AUTH_FPM_KEYGUARD = 4; // FingerprintManager usage from Keyguard
- const int REASON_AUTH_FPM_OTHER = 5; // Other FingerprintManager usage
-
- // Shows the overlay.
+ // Shows the overlay for the given sensor with a reason from BiometricOverlayConstants.
void showUdfpsOverlay(int sensorId, int reason, IUdfpsOverlayControllerCallback callback);
// Hides the overlay.
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index e7e53b39982a..e58419fb688d 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -258,6 +258,13 @@ public final class DeviceConfig {
public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
/**
+ * Namespace for all lmkd related features.
+ *
+ * @hide
+ */
+ public static final String NAMESPACE_LMKD_NATIVE = "lmkd_native";
+
+ /**
* Namespace for all location related features.
*
* @hide
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 8e4a68e52697..40041486f6a6 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -436,7 +436,7 @@ public class StatusBarNotification implements Parcelable {
try {
ApplicationInfo ai = context.getPackageManager()
.getApplicationInfoAsUser(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES,
- getUserId());
+ getNormalizedUserId());
mContext = context.createApplicationContext(ai,
Context.CONTEXT_RESTRICTED);
} catch (PackageManager.NameNotFoundException e) {
diff --git a/core/java/android/view/InsetsVisibilities.java b/core/java/android/view/InsetsVisibilities.java
index 30668bad3424..7d259fb91634 100644
--- a/core/java/android/view/InsetsVisibilities.java
+++ b/core/java/android/view/InsetsVisibilities.java
@@ -116,6 +116,10 @@ public class InsetsVisibilities implements Parcelable {
dest.writeIntArray(mVisibilities);
}
+ public void readFromParcel(@NonNull Parcel in) {
+ in.readIntArray(mVisibilities);
+ }
+
public static final @NonNull Creator<InsetsVisibilities> CREATOR =
new Creator<InsetsVisibilities>() {
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 0fc6b08ae02b..7631269d9c1c 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -374,8 +374,8 @@ public final class WindowManagerImpl implements WindowManager {
currentDisplayInfo = possibleDisplayInfos.get(i);
// Calculate max bounds for this rotation and state.
- Rect maxBounds = new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
- currentDisplayInfo.getNaturalHeight());
+ Rect maxBounds = new Rect(0, 0, currentDisplayInfo.logicalWidth,
+ currentDisplayInfo.logicalHeight);
// Calculate insets for the rotated max bounds.
// TODO(181127261) calculate insets for each display rotation and state.
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index 8b8dba89ea67..69bc1b5f7763 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -88,4 +88,9 @@ oneway interface ITaskOrganizer {
* user has pressed back on the root activity of a task controlled by the task organizer.
*/
void onBackPressedOnTaskRoot(in ActivityManager.RunningTaskInfo taskInfo);
+
+ /**
+ * Called when the IME has drawn on the organized task.
+ */
+ void onImeDrawnOnTask(int taskId);
}
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 8c64474dc887..10d21a0ff003 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -26,6 +26,7 @@ import android.content.pm.ActivityInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.InsetsState;
+import android.view.InsetsVisibilities;
import android.view.WindowManager;
/**
@@ -165,7 +166,13 @@ public final class StartingWindowInfo implements Parcelable {
* TaskSnapshot.
* @hide
*/
- public TaskSnapshot mTaskSnapshot;
+ public TaskSnapshot taskSnapshot;
+
+ /**
+ * The requested insets visibility of the top main window.
+ * @hide
+ */
+ public final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
public StartingWindowInfo() {
@@ -190,7 +197,8 @@ public final class StartingWindowInfo implements Parcelable {
dest.writeTypedObject(mainWindowLayoutParams, flags);
dest.writeInt(splashScreenThemeResId);
dest.writeBoolean(isKeyguardOccluded);
- dest.writeTypedObject(mTaskSnapshot, flags);
+ dest.writeTypedObject(taskSnapshot, flags);
+ requestedVisibilities.writeToParcel(dest, flags);
}
void readFromParcel(@NonNull Parcel source) {
@@ -203,7 +211,8 @@ public final class StartingWindowInfo implements Parcelable {
mainWindowLayoutParams = source.readTypedObject(WindowManager.LayoutParams.CREATOR);
splashScreenThemeResId = source.readInt();
isKeyguardOccluded = source.readBoolean();
- mTaskSnapshot = source.readTypedObject(TaskSnapshot.CREATOR);
+ taskSnapshot = source.readTypedObject(TaskSnapshot.CREATOR);
+ requestedVisibilities.readFromParcel(source);
}
@Override
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 6f250fc0ce7a..845c13d9a01b 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -144,6 +144,10 @@ public class TaskOrganizer extends WindowOrganizer {
@BinderThread
public void onBackPressedOnTaskRoot(@NonNull ActivityManager.RunningTaskInfo taskInfo) {}
+ /** @hide */
+ @BinderThread
+ public void onImeDrawnOnTask(int taskId) {}
+
/**
* Creates a persistent root task in WM for a particular windowing-mode.
* @param displayId The display to create the root task on.
@@ -288,6 +292,11 @@ public class TaskOrganizer extends WindowOrganizer {
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo info) {
mExecutor.execute(() -> TaskOrganizer.this.onBackPressedOnTaskRoot(info));
}
+
+ @Override
+ public void onImeDrawnOnTask(int taskId) {
+ mExecutor.execute(() -> TaskOrganizer.this.onImeDrawnOnTask(taskId));
+ }
};
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 387837d82acf..9d6488d7aa14 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -512,17 +512,16 @@ public final class WindowContainerTransaction implements Parcelable {
* @param fragmentToken2 client assigned unique token to create TaskFragment with specified
* in {@link TaskFragmentCreationParams#getFragmentToken()}. If it is
* {@code null}, the transaction will reset the adjacent TaskFragment.
- * @hide
*/
@NonNull
public WindowContainerTransaction setAdjacentTaskFragments(
@NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2,
- @Nullable TaskFragmentAdjacentOptions options) {
+ @Nullable TaskFragmentAdjacentParams params) {
final HierarchyOp hierarchyOp =
new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS)
.setContainer(fragmentToken1)
.setReparentContainer(fragmentToken2)
- .setLaunchOptions(options != null ? options.toBundle() : null)
+ .setLaunchOptions(params != null ? params.toBundle() : null)
.build();
mHierarchyOps.add(hierarchyOp);
return this;
@@ -1304,9 +1303,8 @@ public final class WindowContainerTransaction implements Parcelable {
/**
* Helper class for building an options Bundle that can be used to set adjacent rules of
* TaskFragments.
- * @hide
*/
- public static class TaskFragmentAdjacentOptions {
+ public static class TaskFragmentAdjacentParams {
private static final String DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL =
"android:transaction.adjacent.option.delay_primary_removal";
private static final String DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL =
@@ -1315,29 +1313,43 @@ public final class WindowContainerTransaction implements Parcelable {
private boolean mDelayPrimaryLastActivityRemoval;
private boolean mDelaySecondaryLastActivityRemoval;
- public TaskFragmentAdjacentOptions() {
+ public TaskFragmentAdjacentParams() {
}
- public TaskFragmentAdjacentOptions(@NonNull Bundle bundle) {
+ public TaskFragmentAdjacentParams(@NonNull Bundle bundle) {
mDelayPrimaryLastActivityRemoval = bundle.getBoolean(
DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL);
mDelaySecondaryLastActivityRemoval = bundle.getBoolean(
DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL);
}
- public void setDelayPrimaryLastActivityRemoval(boolean delay) {
+ /** @see #shouldDelayPrimaryLastActivityRemoval() */
+ public void setShouldDelayPrimaryLastActivityRemoval(boolean delay) {
mDelayPrimaryLastActivityRemoval = delay;
}
- public void setDelaySecondaryLastActivityRemoval(boolean delay) {
+ /** @see #shouldDelaySecondaryLastActivityRemoval() */
+ public void setShouldDelaySecondaryLastActivityRemoval(boolean delay) {
mDelaySecondaryLastActivityRemoval = delay;
}
- public boolean isDelayPrimaryLastActivityRemoval() {
+ /**
+ * Whether to delay the last activity of the primary adjacent TaskFragment being immediately
+ * removed while finishing.
+ * <p>
+ * It is usually set to {@code true} to give organizer an opportunity to perform other
+ * actions or animations. An example is to finish together with the adjacent TaskFragment.
+ * </p>
+ */
+ public boolean shouldDelayPrimaryLastActivityRemoval() {
return mDelayPrimaryLastActivityRemoval;
}
- public boolean isDelaySecondaryLastActivityRemoval() {
+ /**
+ * Similar to {@link #shouldDelayPrimaryLastActivityRemoval()}, but for the secondary
+ * TaskFragment.
+ */
+ public boolean shouldDelaySecondaryLastActivityRemoval() {
return mDelaySecondaryLastActivityRemoval;
}
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
index 0b92b93f2c88..874e3f4ae26a 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityShortcutChooserActivity.java
@@ -47,6 +47,8 @@ import java.util.List;
public class AccessibilityShortcutChooserActivity extends Activity {
@ShortcutType
private final int mShortcutType = ACCESSIBILITY_SHORTCUT_KEY;
+ private static final String KEY_ACCESSIBILITY_SHORTCUT_MENU_MODE =
+ "accessibility_shortcut_menu_mode";
private final List<AccessibilityTarget> mTargets = new ArrayList<>();
private AlertDialog mMenuDialog;
private AlertDialog mPermissionDialog;
@@ -66,14 +68,30 @@ public class AccessibilityShortcutChooserActivity extends Activity {
mMenuDialog = createMenuDialog();
mMenuDialog.setOnShowListener(dialog -> updateDialogListeners());
mMenuDialog.show();
+
+ if (savedInstanceState != null) {
+ final int restoreShortcutMenuMode =
+ savedInstanceState.getInt(KEY_ACCESSIBILITY_SHORTCUT_MENU_MODE,
+ ShortcutMenuMode.LAUNCH);
+ if (restoreShortcutMenuMode == ShortcutMenuMode.EDIT) {
+ onEditButtonClicked();
+ }
+ }
}
@Override
protected void onDestroy() {
+ mMenuDialog.setOnDismissListener(null);
mMenuDialog.dismiss();
super.onDestroy();
}
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(KEY_ACCESSIBILITY_SHORTCUT_MENU_MODE, mTargetAdapter.getShortcutMenuMode());
+ }
+
private void onTargetSelected(AdapterView<?> parent, View view, int position, long id) {
final AccessibilityTarget target = mTargets.get(position);
target.onSelected();
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index ce75f45d0897..068b882eb4f7 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -165,7 +165,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
// Now fetch app icon and raster with no badging even in work profile
Bitmap appIcon = mSelectableTargetInfoCommunicator.makePresentationGetter(info)
- .getIconBitmap(android.os.Process.myUserHandle());
+ .getIconBitmap(mContext.getUser());
// Raster target drawable with appIcon as a badge
SimpleIconFactory sif = SimpleIconFactory.obtain(mContext);
diff --git a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java
index 4ce6f609ef73..3eb980465214 100644
--- a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java
+++ b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java
@@ -17,19 +17,27 @@
package com.android.internal.notification;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import com.android.internal.R;
+
+/**
+ * This class provides methods to create intents for NotificationAccessConfirmationActivity.
+ */
public final class NotificationAccessConfirmationActivityContract {
- private static final ComponentName COMPONENT_NAME = new ComponentName(
- "com.android.settings",
- "com.android.settings.notification.NotificationAccessConfirmationActivity");
public static final String EXTRA_USER_ID = "user_id";
public static final String EXTRA_COMPONENT_NAME = "component_name";
public static final String EXTRA_PACKAGE_TITLE = "package_title";
- public static Intent launcherIntent(int userId, ComponentName component, String packageTitle) {
+ /**
+ * Creates a launcher intent for NotificationAccessConfirmationActivity.
+ */
+ public static Intent launcherIntent(Context context, int userId, ComponentName component,
+ String packageTitle) {
return new Intent()
- .setComponent(COMPONENT_NAME)
+ .setComponent(ComponentName.unflattenFromString(context.getString(
+ R.string.config_notificationAccessConfirmationActivity)))
.putExtra(EXTRA_USER_ID, userId)
.putExtra(EXTRA_COMPONENT_NAME, component)
.putExtra(EXTRA_PACKAGE_TITLE, packageTitle);
diff --git a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
index b321ac08912b..a09c8236b47d 100644
--- a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
+++ b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
@@ -124,7 +124,6 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
- sendCloseSystemWindows();
mContext.startActivity(intent);
} catch (ActivityNotFoundException e) {
startCallActivity();
@@ -147,7 +146,6 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
dispatcher.performedLongPress(event);
if (isUserSetupComplete()) {
mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- sendCloseSystemWindows();
// Broadcast an intent that the Camera button was longpressed
Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -178,7 +176,6 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- sendCloseSystemWindows();
getSearchManager().stopSearch();
mContext.startActivity(intent);
// Only clear this if we successfully start the
@@ -272,7 +269,6 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
@UnsupportedAppUsage
void startCallActivity() {
- sendCloseSystemWindows();
Intent intent = new Intent(Intent.ACTION_CALL_BUTTON);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
@@ -319,10 +315,6 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
return mMediaSessionManager;
}
- void sendCloseSystemWindows() {
- PhoneWindow.sendCloseSystemWindows(mContext, null);
- }
-
private void handleVolumeKeyEvent(KeyEvent keyEvent) {
getMediaSessionManager().dispatchVolumeKeyEventAsSystemService(keyEvent,
AudioManager.USE_DEFAULT_STREAM_TYPE);
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index f040462dafdc..5144a91706a1 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -14,15 +14,20 @@
package com.android.internal.util;
+import static android.os.Trace.TRACE_TAG_APP;
+
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.os.Build;
import android.os.SystemClock;
import android.os.Trace;
import android.provider.DeviceConfig;
+import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
-import android.util.SparseLongArray;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.EventLogTags;
@@ -31,6 +36,7 @@ import com.android.internal.os.BackgroundThread;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
/**
* Class to track various latencies in SystemUI. It then writes the latency to statsd and also
@@ -44,6 +50,7 @@ public class LatencyTracker {
private static final String TAG = "LatencyTracker";
private static final String SETTINGS_ENABLED_KEY = "enabled";
private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+ private static final boolean DEBUG = false;
/** Default to being enabled on debug builds. */
private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE;
/** Default to collecting data for 1/5 of all actions (randomly sampled). */
@@ -162,7 +169,8 @@ public class LatencyTracker {
private static LatencyTracker sLatencyTracker;
private final Object mLock = new Object();
- private final SparseLongArray mStartRtc = new SparseLongArray();
+ @GuardedBy("mLock")
+ private final SparseArray<Session> mSessions = new SparseArray<>();
@GuardedBy("mLock")
private final int[] mTraceThresholdPerAction = new int[ACTIONS_ALL.length];
@GuardedBy("mLock")
@@ -244,8 +252,12 @@ public class LatencyTracker {
}
}
- private static String getTraceNameOfAction(@Action int action) {
- return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">";
+ private static String getTraceNameOfAction(@Action int action, String tag) {
+ if (TextUtils.isEmpty(tag)) {
+ return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">";
+ } else {
+ return "L<" + getNameOfAction(STATSD_ACTION[action]) + "::" + tag + ">";
+ }
}
private static String getTraceTriggerNameForAction(@Action int action) {
@@ -263,35 +275,82 @@ public class LatencyTracker {
}
/**
- * Notifies that an action is starting. This needs to be called from the main thread.
+ * Notifies that an action is starting. <s>This needs to be called from the main thread.</s>
*
* @param action The action to start. One of the ACTION_* values.
*/
public void onActionStart(@Action int action) {
- if (!isEnabled()) {
- return;
+ onActionStart(action, null);
+ }
+
+ /**
+ * Notifies that an action is starting. <s>This needs to be called from the main thread.</s>
+ *
+ * @param action The action to start. One of the ACTION_* values.
+ * @param tag The brief description of the action.
+ */
+ public void onActionStart(@Action int action, String tag) {
+ synchronized (mLock) {
+ if (!isEnabled()) {
+ return;
+ }
+ // skip if the action is already instrumenting.
+ if (mSessions.get(action) != null) {
+ return;
+ }
+ Session session = new Session(action, tag);
+ session.begin(() -> onActionCancel(action));
+ mSessions.put(action, session);
+
+ if (DEBUG) {
+ Log.d(TAG, "onActionStart: " + session.name() + ", start=" + session.mStartRtc);
+ }
}
- Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, getTraceNameOfAction(action), 0);
- mStartRtc.put(action, SystemClock.elapsedRealtime());
}
/**
- * Notifies that an action has ended. This needs to be called from the main thread.
+ * Notifies that an action has ended. <s>This needs to be called from the main thread.</s>
*
* @param action The action to end. One of the ACTION_* values.
*/
public void onActionEnd(@Action int action) {
- if (!isEnabled()) {
- return;
+ synchronized (mLock) {
+ if (!isEnabled()) {
+ return;
+ }
+ Session session = mSessions.get(action);
+ if (session == null) {
+ return;
+ }
+ session.end();
+ mSessions.delete(action);
+ logAction(action, session.duration());
+
+ if (DEBUG) {
+ Log.d(TAG, "onActionEnd:" + session.name() + ", duration=" + session.duration());
+ }
}
- long endRtc = SystemClock.elapsedRealtime();
- long startRtc = mStartRtc.get(action, -1);
- if (startRtc == -1) {
- return;
+ }
+
+ /**
+ * Notifies that an action has canceled. <s>This needs to be called from the main thread.</s>
+ *
+ * @param action The action to cancel. One of the ACTION_* values.
+ * @hide
+ */
+ public void onActionCancel(@Action int action) {
+ synchronized (mLock) {
+ Session session = mSessions.get(action);
+ if (session == null) {
+ return;
+ }
+ session.cancel();
+ mSessions.delete(action);
+
+ if (DEBUG) {
+ Log.d(TAG, "onActionCancel: " + session.name());
+ }
}
- mStartRtc.delete(action);
- Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, getTraceNameOfAction(action), 0);
- logAction(action, (int) (endRtc - startRtc));
}
/**
@@ -332,4 +391,57 @@ public class LatencyTracker {
FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration);
}
}
+
+ static class Session {
+ @Action
+ private final int mAction;
+ private final String mTag;
+ private final String mName;
+ private Runnable mTimeoutRunnable;
+ private long mStartRtc = -1;
+ private long mEndRtc = -1;
+
+ Session(@Action int action, @Nullable String tag) {
+ mAction = action;
+ mTag = tag;
+ mName = TextUtils.isEmpty(mTag)
+ ? getNameOfAction(STATSD_ACTION[mAction])
+ : getNameOfAction(STATSD_ACTION[mAction]) + "::" + mTag;
+ }
+
+ String name() {
+ return mName;
+ }
+
+ String traceName() {
+ return getTraceNameOfAction(mAction, mTag);
+ }
+
+ void begin(@NonNull Runnable timeoutAction) {
+ mStartRtc = SystemClock.elapsedRealtime();
+ Trace.asyncTraceBegin(TRACE_TAG_APP, traceName(), 0);
+
+ // start counting timeout.
+ mTimeoutRunnable = timeoutAction;
+ BackgroundThread.getHandler()
+ .postDelayed(mTimeoutRunnable, TimeUnit.SECONDS.toMillis(2));
+ }
+
+ void end() {
+ mEndRtc = SystemClock.elapsedRealtime();
+ Trace.asyncTraceEnd(TRACE_TAG_APP, traceName(), 0);
+ BackgroundThread.getHandler().removeCallbacks(mTimeoutRunnable);
+ mTimeoutRunnable = null;
+ }
+
+ void cancel() {
+ Trace.asyncTraceEnd(TRACE_TAG_APP, traceName(), 0);
+ BackgroundThread.getHandler().removeCallbacks(mTimeoutRunnable);
+ mTimeoutRunnable = null;
+ }
+
+ int duration() {
+ return (int) (mEndRtc - mStartRtc);
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 498505cd46ff..cd1bbb6bc6fe 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -17,6 +17,7 @@
package com.android.internal.widget;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
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;
@@ -1272,6 +1273,14 @@ public class LockPatternUtils {
}
/**
+ * Whether the user is not allowed to set any credentials via PASSWORD_QUALITY_MANAGED.
+ */
+ public boolean isCredentialsDisabledForUser(int userId) {
+ return getDevicePolicyManager().getPasswordQuality(/* admin= */ null, userId)
+ == PASSWORD_QUALITY_MANAGED;
+ }
+
+ /**
* @see StrongAuthTracker#isTrustAllowedForUser
*/
public boolean isTrustAllowedForUser(int userId) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e53d379ea92a..63d61fc5ef9e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5876,7 +5876,6 @@
android:excludeFromRecents="true"
android:documentLaunchMode="never"
android:relinquishTaskIdentity="true"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
android:process=":ui"
android:visibleToInstantApps="true">
<intent-filter>
diff --git a/core/res/res/drawable-nodpi/default_wallpaper.png b/core/res/res/drawable-nodpi/default_wallpaper.png
index ce546f0a11e7..490ebeeb75c1 100644
--- a/core/res/res/drawable-nodpi/default_wallpaper.png
+++ b/core/res/res/drawable-nodpi/default_wallpaper.png
Binary files differ
diff --git a/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png b/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png
deleted file mode 100644
index af8e2512385a..000000000000
--- a/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png b/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png
deleted file mode 100644
index cb00d82a826f..000000000000
--- a/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/layout/accessibility_shortcut_chooser_item.xml b/core/res/res/layout/accessibility_shortcut_chooser_item.xml
index 7cca1292af68..4d7946b2138b 100644
--- a/core/res/res/layout/accessibility_shortcut_chooser_item.xml
+++ b/core/res/res/layout/accessibility_shortcut_chooser_item.xml
@@ -39,15 +39,20 @@
android:layout_height="48dp"
android:scaleType="fitCenter"/>
- <TextView
- android:id="@+id/accessibility_shortcut_target_label"
+ <LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:layout_weight="1"
- android:textSize="20sp"
- android:textColor="?attr/textColorPrimary"
- android:fontFamily="sans-serif-medium"/>
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/accessibility_shortcut_target_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="20sp"
+ android:textColor="?attr/textColorPrimary"
+ android:fontFamily="sans-serif-medium"/>
+ </LinearLayout>
<TextView
android:id="@+id/accessibility_shortcut_target_status"
diff --git a/core/res/res/layout/transient_notification.xml b/core/res/res/layout/transient_notification.xml
index 8a3d73491af3..3259201f6e9b 100644
--- a/core/res/res/layout/transient_notification.xml
+++ b/core/res/res/layout/transient_notification.xml
@@ -40,6 +40,5 @@
android:maxLines="2"
android:paddingTop="12dp"
android:paddingBottom="12dp"
- android:lineHeight="20sp"
android:textAppearance="@style/TextAppearance.Toast"/>
</LinearLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d5f4cad699c5..985d81099538 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3219,6 +3219,11 @@
and one pSIM) -->
<integer name="config_num_physical_slots">1</integer>
+ <!-- When a radio power off request is received, we will delay completing the request until
+ either IMS moves to the deregistered state or the timeout defined by this configuration
+ elapses. If 0, this feature is disabled and we do not delay radio power off requests.-->
+ <integer name="config_delay_for_ims_dereg_millis">0</integer>
+
<!--Thresholds for LTE dbm in status bar-->
<integer-array translatable="false" name="config_lteDbmThresholds">
<item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
@@ -4576,6 +4581,20 @@
-->
</integer-array>
+ <!-- An array of arrays of side fingerprint sensor properties relative to each display.
+ Note: this value is temporary and is expected to be queried directly
+ from the HAL in the future. -->
+ <array name="config_sfps_sensor_props" translatable="false">
+ <!--
+ <array>
+ <item>displayId</item>
+ <item>sensorLocationX</item>
+ <item>sensorLocationY</item>
+ <item>sensorRadius</item>
+ <array>
+ -->
+ </array>
+
<!-- How long it takes for the HW to start illuminating after the illumination is requested. -->
<integer name="config_udfps_illumination_transition_ms">50</integer>
@@ -5174,6 +5193,12 @@
<item>@array/config_secondaryBuiltInDisplayWaterfallCutout</item>
</array>
+ <!-- The component name of the activity for the companion-device-manager notification access
+ confirmation. -->
+ <string name="config_notificationAccessConfirmationActivity" translatable="false">
+ com.android.settings/com.android.settings.notification.NotificationAccessConfirmationActivity
+ </string>
+
<!-- Whether the airplane mode should be reset when device boots in non-safemode after exiting
from safemode.
This flag should be enabled only when the product does not have any UI to toggle airplane
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index bac9cf23b176..9553f95fe5c9 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -966,6 +966,7 @@ please see styles_device_defaults.xml.
<style name="TextAppearance.Toast">
<item name="fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="textSize">14sp</item>
+ <item name="lineHeight">20sp</item>
<item name="textColor">?android:attr/textColorPrimary</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7fe73e5067b9..97d5e28036c2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -486,6 +486,7 @@
<java-symbol type="string" name="config_deviceSpecificDevicePolicyManagerService" />
<java-symbol type="string" name="config_deviceSpecificAudioService" />
<java-symbol type="integer" name="config_num_physical_slots" />
+ <java-symbol type="integer" name="config_delay_for_ims_dereg_millis" />
<java-symbol type="array" name="config_integrityRuleProviderPackages" />
<java-symbol type="bool" name="config_useAssistantVolume" />
<java-symbol type="string" name="config_bandwidthEstimateSource" />
@@ -2221,6 +2222,7 @@
<java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneProvider" />
<java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" />
<java-symbol type="bool" name="config_autoResetAirplaneMode" />
+ <java-symbol type="string" name="config_notificationAccessConfirmationActivity" />
<java-symbol type="layout" name="resolver_list" />
<java-symbol type="id" name="resolver_list" />
@@ -2608,6 +2610,7 @@
<java-symbol type="array" name="config_biometric_sensors" />
<java-symbol type="bool" name="allow_test_udfps" />
<java-symbol type="array" name="config_udfps_sensor_props" />
+ <java-symbol type="array" name="config_sfps_sensor_props" />
<java-symbol type="integer" name="config_udfps_illumination_transition_ms" />
<java-symbol type="bool" name="config_is_powerbutton_fps" />
diff --git a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
index a5a98a98f0be..109b7ab73d6f 100644
--- a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
+++ b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
@@ -17,6 +17,8 @@
package android.service.notification;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertFalse;
@@ -51,6 +53,7 @@ public class StatusBarNotificationTest {
private final Context mMockContext = mock(Context.class);
@Mock
+ private Context mRealContext;
private PackageManager mPm;
private static final String PKG = "com.example.o";
@@ -75,6 +78,8 @@ public class StatusBarNotificationTest {
InstrumentationRegistry.getContext().getResources());
when(mMockContext.getPackageManager()).thenReturn(mPm);
when(mMockContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+
+ mRealContext = InstrumentationRegistry.getContext();
}
@Test
@@ -199,6 +204,19 @@ public class StatusBarNotificationTest {
}
+ @Test
+ public void testGetPackageContext_worksWithUserAll() {
+ String pkg = "com.android.systemui";
+ int uid = 1000;
+ Notification notification = getNotificationBuilder(GROUP_ID_1, CHANNEL_ID).build();
+ StatusBarNotification sbn = new StatusBarNotification(
+ pkg, pkg, ID, TAG, uid, uid, notification, UserHandle.ALL, null, UID);
+ Context resultContext = sbn.getPackageContext(mRealContext);
+ assertNotNull(resultContext);
+ assertNotSame(mRealContext, resultContext);
+ assertEquals(pkg, resultContext.getPackageName());
+ }
+
private StatusBarNotification getNotification(String pkg, String group, String channelId) {
return getNotification(pkg, getNotificationBuilder(group, channelId));
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 0d7225be9e03..119c939135a6 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1207,6 +1207,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-779535710": {
+ "message": "Transition %d: Set %s as transient-launch",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/Transition.java"
+ },
"-775004869": {
"message": "Not a match: %s",
"level": "DEBUG",
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
index 9212a0f5e6b9..46c8ffe286bd 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
@@ -210,17 +210,17 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
@NonNull IBinder primary, @Nullable IBinder secondary, @Nullable SplitRule splitRule) {
- WindowContainerTransaction.TaskFragmentAdjacentOptions adjacentOptions = null;
+ WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams = null;
final boolean finishSecondaryWithPrimary =
splitRule != null && SplitContainer.shouldFinishSecondaryWithPrimary(splitRule);
final boolean finishPrimaryWithSecondary =
splitRule != null && SplitContainer.shouldFinishPrimaryWithSecondary(splitRule);
if (finishSecondaryWithPrimary || finishPrimaryWithSecondary) {
- adjacentOptions = new WindowContainerTransaction.TaskFragmentAdjacentOptions();
- adjacentOptions.setDelayPrimaryLastActivityRemoval(finishSecondaryWithPrimary);
- adjacentOptions.setDelaySecondaryLastActivityRemoval(finishPrimaryWithSecondary);
+ adjacentParams = new WindowContainerTransaction.TaskFragmentAdjacentParams();
+ adjacentParams.setShouldDelayPrimaryLastActivityRemoval(finishSecondaryWithPrimary);
+ adjacentParams.setShouldDelaySecondaryLastActivityRemoval(finishPrimaryWithSecondary);
}
- wct.setAdjacentTaskFragments(primary, secondary, adjacentOptions);
+ wct.setAdjacentTaskFragments(primary, secondary, adjacentParams);
}
TaskFragmentCreationParams createFragmentOptions(IBinder fragmentToken, IBinder ownerToken,
diff --git a/libs/WindowManager/Shell/res/drawable/split_rounded_bottom.xml b/libs/WindowManager/Shell/res/drawable/split_rounded_bottom.xml
deleted file mode 100644
index 18dc909ae955..000000000000
--- a/libs/WindowManager/Shell/res/drawable/split_rounded_bottom.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/split_divider_corner_size"
- android:height="@dimen/split_divider_corner_size"
- android:viewportWidth="42"
- android:viewportHeight="42">
-
- <group android:pivotX="21"
- android:pivotY="21"
- android:rotation="180">
- <path
- android:fillColor="@color/split_divider_background"
- android:pathData="m 0 0 c 8 0 16 8 16 16 h 10 c 0 -8 8 -16 16 -16 z" />
- </group>
-
-</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/split_rounded_left.xml b/libs/WindowManager/Shell/res/drawable/split_rounded_left.xml
deleted file mode 100644
index 931cacf887cd..000000000000
--- a/libs/WindowManager/Shell/res/drawable/split_rounded_left.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/split_divider_corner_size"
- android:height="@dimen/split_divider_corner_size"
- android:viewportWidth="42"
- android:viewportHeight="42">
-
- <group android:pivotX="21"
- android:pivotY="21"
- android:rotation="-90">
- <path
- android:fillColor="@color/split_divider_background"
- android:pathData="m 0 0 c 8 0 16 8 16 16 h 10 c 0 -8 8 -16 16 -16 z" />
- </group>
-
-</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/split_rounded_right.xml b/libs/WindowManager/Shell/res/drawable/split_rounded_right.xml
deleted file mode 100644
index 54e47612faa8..000000000000
--- a/libs/WindowManager/Shell/res/drawable/split_rounded_right.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/split_divider_corner_size"
- android:height="@dimen/split_divider_corner_size"
- android:viewportWidth="42"
- android:viewportHeight="42">
-
- <group android:pivotX="21"
- android:pivotY="21"
- android:rotation="90">
- <path
- android:fillColor="@color/split_divider_background"
- android:pathData="m 0 0 c 8 0 16 8 16 16 h 10 c 0 -8 8 -16 16 -16 z" />
- </group>
-
-</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/split_rounded_top.xml b/libs/WindowManager/Shell/res/drawable/split_rounded_top.xml
deleted file mode 100644
index 9115b5a2352e..000000000000
--- a/libs/WindowManager/Shell/res/drawable/split_rounded_top.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/split_divider_corner_size"
- android:height="@dimen/split_divider_corner_size"
- android:viewportWidth="42"
- android:viewportHeight="42">
-
- <path
- android:fillColor="@color/split_divider_background"
- android:pathData="m 0 0 c 8 0 16 8 16 16 h 10 c 0 -8 8 -16 16 -16 z" />
-
-</vector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/split_divider.xml b/libs/WindowManager/Shell/res/layout/split_divider.xml
index 94182cdba0dd..e3be700469a7 100644
--- a/libs/WindowManager/Shell/res/layout/split_divider.xml
+++ b/libs/WindowManager/Shell/res/layout/split_divider.xml
@@ -24,20 +24,20 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <View style="@style/DockedDividerTopLeftRoundCorner"/>
-
<View
style="@style/DockedDividerBackground"
android:id="@+id/docked_divider_background"/>
- <View style="@style/DockedDividerBottomRightRoundCorner"/>
-
<com.android.wm.shell.common.split.DividerHandleView
style="@style/DockedDividerHandle"
android:id="@+id/docked_divider_handle"
android:contentDescription="@string/accessibility_divider"
android:background="@null"/>
+ <com.android.wm.shell.common.split.DividerRoundedCorner
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
</FrameLayout>
</com.android.wm.shell.common.split.DividerView>
diff --git a/libs/WindowManager/Shell/res/values-land/styles.xml b/libs/WindowManager/Shell/res/values-land/styles.xml
index e5707f3170d8..9eddac48e6de 100644
--- a/libs/WindowManager/Shell/res/values-land/styles.xml
+++ b/libs/WindowManager/Shell/res/values-land/styles.xml
@@ -28,20 +28,6 @@
<item name="android:layout_height">96dp</item>
</style>
- <style name="DockedDividerTopLeftRoundCorner">
- <item name="android:layout_gravity">center_horizontal|top</item>
- <item name="android:background">@drawable/split_rounded_top</item>
- <item name="android:layout_width">@dimen/split_divider_corner_size</item>
- <item name="android:layout_height">@dimen/split_divider_corner_size</item>
- </style>
-
- <style name="DockedDividerBottomRightRoundCorner">
- <item name="android:layout_gravity">center_horizontal|bottom</item>
- <item name="android:background">@drawable/split_rounded_bottom</item>
- <item name="android:layout_width">@dimen/split_divider_corner_size</item>
- <item name="android:layout_height">@dimen/split_divider_corner_size</item>
- </style>
-
<style name="DockedDividerMinimizedShadow">
<item name="android:layout_width">8dp</item>
<item name="android:layout_height">match_parent</item>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 28ff25ae0fbe..cb6d4de71a45 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -37,20 +37,6 @@
<item name="android:background">@color/split_divider_background</item>
</style>
- <style name="DockedDividerTopLeftRoundCorner">
- <item name="android:layout_gravity">center_vertical|left</item>
- <item name="android:background">@drawable/split_rounded_left</item>
- <item name="android:layout_width">@dimen/split_divider_corner_size</item>
- <item name="android:layout_height">@dimen/split_divider_corner_size</item>
- </style>
-
- <style name="DockedDividerBottomRightRoundCorner">
- <item name="android:layout_gravity">center_vertical|right</item>
- <item name="android:background">@drawable/split_rounded_right</item>
- <item name="android:layout_width">@dimen/split_divider_corner_size</item>
- <item name="android:layout_height">@dimen/split_divider_corner_size</item>
- </style>
-
<style name="DockedDividerMinimizedShadow">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">8dp</item>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index b5dffba7a0f3..d3265346036a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -340,6 +340,13 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
@Override
+ public void onImeDrawnOnTask(int taskId) {
+ if (mStartingWindow != null) {
+ mStartingWindow.onImeDrawnOnTask(taskId);
+ }
+ }
+
+ @Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
synchronized (mLock) {
onTaskAppeared(new TaskAppearedInfo(taskInfo, leash));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index 3800b8d234f3..c2cb72a530a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -43,6 +43,7 @@ import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.SurfaceUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.common.split.SplitWindowManager;
import java.io.PrintWriter;
@@ -70,6 +71,19 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou
private final DisplayImeController mDisplayImeController;
private SplitLayout mSplitLayout;
+ private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
+ new SplitWindowManager.ParentContainerCallbacks() {
+ @Override
+ public void attachToParentSurface(SurfaceControl.Builder b) {
+ b.setParent(mRootTaskLeash);
+ }
+
+ @Override
+ public void onLeashReady(SurfaceControl leash) {
+ mSyncQueue.runInSync(t -> t.show(leash));
+ }
+ };
+
AppPair(AppPairsController controller) {
mController = controller;
mSyncQueue = controller.getSyncTransactionQueue();
@@ -110,8 +124,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou
mSplitLayout = new SplitLayout(TAG + "SplitDivider",
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
mRootTaskInfo.configuration, this /* layoutChangeListener */,
- b -> b.setParent(mRootTaskLeash), mDisplayImeController,
- mController.getTaskOrganizer());
+ mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer());
final WindowContainerToken token1 = task1.token;
final WindowContainerToken token2 = task2.token;
@@ -218,8 +231,6 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou
if (mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
onLayoutChanged(mSplitLayout);
}
- // updateConfiguration re-inits the dividerbar, so show it now
- mSyncQueue.runInSync(t -> t.show(mSplitLayout.getDividerLeash()));
}
} else if (taskInfo.taskId == getTaskId1()) {
mTaskInfo1 = taskInfo;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 9d65d28b21b4..05ebbba4e955 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -422,14 +422,6 @@ public class Bubble implements BubbleViewProvider {
}
}
- @Override
- public void setExpandedContentAlpha(float alpha) {
- if (mExpandedView != null) {
- mExpandedView.setAlpha(alpha);
- mExpandedView.setTaskViewAlpha(alpha);
- }
- }
-
/**
* Set visibility of bubble in the expanded state.
*
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 9dafefebf62b..c126f32387f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -69,6 +69,7 @@ import android.util.SparseArray;
import android.util.SparseSetArray;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.window.WindowContainerTransaction;
@@ -144,7 +145,6 @@ public class BubbleController {
private BubbleLogger mLogger;
private BubbleData mBubbleData;
- private View mBubbleScrim;
@Nullable private BubbleStackView mStackView;
private BubbleIconFactory mBubbleIconFactory;
private BubblePositioner mBubblePositioner;
@@ -189,6 +189,9 @@ public class BubbleController {
/** Saved direction, used to detect layout direction changes @link #onConfigChanged}. */
private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED;
+ /** Saved insets, used to detect WindowInset changes. */
+ private WindowInsets mWindowInsets;
+
private boolean mInflateSynchronously;
/** True when user is in status bar unlock shade. */
@@ -629,8 +632,11 @@ public class BubbleController {
mBubbleData.getOverflow().initialize(this);
mWindowManager.addView(mStackView, mWmLayoutParams);
mStackView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
- mBubblePositioner.update();
- mStackView.onDisplaySizeChanged();
+ if (!windowInsets.equals(mWindowInsets)) {
+ mWindowInsets = windowInsets;
+ mBubblePositioner.update();
+ mStackView.onDisplaySizeChanged();
+ }
return windowInsets;
});
} catch (IllegalStateException e) {
@@ -1372,8 +1378,9 @@ public class BubbleController {
private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedTaskListener {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+ mBubblePositioner.setImeVisible(imeVisible, imeHeight);
if (mStackView != null) {
- mStackView.onImeVisibilityChanged(imeVisible, imeHeight);
+ mStackView.animateForIme(imeVisible);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 7d7bfb2a92a3..a87aad4261a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -398,6 +398,7 @@ public class BubbleExpandedView extends LinearLayout {
updatePointerView();
}
+ /** Updates the size and visuals of the pointer. **/
private void updatePointerView() {
LayoutParams lp = (LayoutParams) mPointerView.getLayoutParams();
if (mCurrentPointer == mLeftPointer || mCurrentPointer == mRightPointer) {
@@ -524,9 +525,8 @@ public class BubbleExpandedView extends LinearLayout {
if (mTaskView != null) {
mTaskView.setAlpha(alpha);
}
- if (mManageButton != null && mManageButton.getVisibility() == View.VISIBLE) {
- mManageButton.setAlpha(alpha);
- }
+ mPointerView.setAlpha(alpha);
+ setAlpha(alpha);
}
/**
@@ -545,6 +545,7 @@ public class BubbleExpandedView extends LinearLayout {
mIsContentVisible = visibility;
if (mTaskView != null && !mIsAlphaAnimating) {
mTaskView.setAlpha(visibility ? 1f : 0f);
+ mPointerView.setAlpha(visibility ? 1f : 0f);
}
}
@@ -689,7 +690,7 @@ public class BubbleExpandedView extends LinearLayout {
* the bubble if showing vertically.
* @param onLeft whether the stack was on the left side of the screen when expanded.
*/
- public void setPointerPosition(float bubblePosition, boolean onLeft) {
+ public void setPointerPosition(float bubblePosition, boolean onLeft, boolean animate) {
// Pointer gets drawn in the padding
final boolean showVertically = mPositioner.showBubblesVertically();
final float paddingLeft = (showVertically && onLeft)
@@ -710,6 +711,8 @@ public class BubbleExpandedView extends LinearLayout {
: pointerPosition;
// Post because we need the width of the view
post(() -> {
+ mCurrentPointer = showVertically ? onLeft ? mLeftPointer : mRightPointer : mTopPointer;
+ updatePointerView();
float pointerY;
float pointerX;
if (showVertically) {
@@ -721,11 +724,13 @@ public class BubbleExpandedView extends LinearLayout {
pointerY = mPointerOverlap;
pointerX = bubbleCenter - (mPointerWidth / 2f);
}
- mPointerView.setTranslationY(pointerY);
- mPointerView.setTranslationX(pointerX);
- mCurrentPointer = showVertically ? onLeft ? mLeftPointer : mRightPointer : mTopPointer;
- updatePointerView();
- mPointerView.setVisibility(VISIBLE);
+ if (animate) {
+ mPointerView.animate().translationX(pointerX).translationY(pointerY).start();
+ } else {
+ mPointerView.setTranslationY(pointerY);
+ mPointerView.setTranslationX(pointerX);
+ mPointerView.setVisibility(VISIBLE);
+ }
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 705a12a5e65b..0c3a6b2dbd84 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -154,10 +154,6 @@ class BubbleOverflow(
return dotPath
}
- override fun setExpandedContentAlpha(alpha: Float) {
- expandedView?.alpha = alpha
- }
-
override fun setTaskViewVisibility(visible: Boolean) {
// Overflow does not have a TaskView.
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 306224bd316c..127d5a8a9966 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -70,13 +70,16 @@ public class BubblePositioner {
private Context mContext;
private WindowManager mWindowManager;
- private Rect mPositionRect;
private Rect mScreenRect;
private @Surface.Rotation int mRotation = Surface.ROTATION_0;
private Insets mInsets;
+ private boolean mImeVisible;
+ private int mImeHeight;
+ private boolean mIsLargeScreen;
+
+ private Rect mPositionRect;
private int mDefaultMaxBubbles;
private int mMaxBubbles;
-
private int mBubbleSize;
private int mSpacingBetweenBubbles;
@@ -98,7 +101,6 @@ public class BubblePositioner {
private PointF mRestingStackPosition;
private int[] mPaddings = new int[4];
- private boolean mIsLargeScreen;
private boolean mShowingInTaskbar;
private @TaskbarPosition int mTaskbarPosition = TASKBAR_POSITION_NONE;
private int mTaskbarIconSize;
@@ -302,6 +304,17 @@ public class BubblePositioner {
return mMaxBubbles;
}
+ /** The height for the IME if it's visible. **/
+ public int getImeHeight() {
+ return mImeVisible ? mImeHeight : 0;
+ }
+
+ /** Sets whether the IME is visible. **/
+ public void setImeVisible(boolean visible, int height) {
+ mImeVisible = visible;
+ mImeHeight = height;
+ }
+
/**
* Calculates the padding for the bubble expanded view.
*
@@ -357,7 +370,7 @@ public class BubblePositioner {
}
/** Gets the y position of the expanded view if it was top-aligned. */
- private float getExpandedViewYTopAligned() {
+ public float getExpandedViewYTopAligned() {
final int top = getAvailableRect().top;
if (showBubblesVertically()) {
return top - mPointerWidth + mExpandedViewPadding;
@@ -366,10 +379,6 @@ public class BubblePositioner {
}
}
- public float getExpandedBubblesY() {
- return getAvailableRect().top + mExpandedViewPadding;
- }
-
/**
* Calculate the maximum height the expanded view can be depending on where it's placed on
* the screen and the size of the elements around it (e.g. padding, pointer, manage button).
@@ -464,18 +473,21 @@ public class BubblePositioner {
: bubblePosition + (normalizedSize / 2f) - mPointerWidth;
}
+ private int getExpandedStackSize(int numberOfBubbles) {
+ return (numberOfBubbles * mBubbleSize)
+ + ((numberOfBubbles - 1) * mSpacingBetweenBubbles);
+ }
+
/**
* Returns the position of the bubble on-screen when the stack is expanded.
*
* @param index the index of the bubble in the stack.
- * @param numberOfBubbles the total number of bubbles in the stack.
- * @param onLeftEdge whether the stack would rest on the left edge of the screen when collapsed.
- * @return the x, y position of the bubble on-screen when the stack is expanded.
+ * @param state state information about the stack to help with calculations.
+ * @return the position of the bubble on-screen when the stack is expanded.
*/
- public PointF getExpandedBubbleXY(int index, int numberOfBubbles, boolean onLeftEdge) {
+ public PointF getExpandedBubbleXY(int index, BubbleStackView.StackViewState state) {
final float positionInRow = index * (mBubbleSize + mSpacingBetweenBubbles);
- final float expandedStackSize = (numberOfBubbles * mBubbleSize)
- + ((numberOfBubbles - 1) * mSpacingBetweenBubbles);
+ final float expandedStackSize = getExpandedStackSize(state.numberOfBubbles);
final float centerPosition = showBubblesVertically()
? mPositionRect.centerY()
: mPositionRect.centerX();
@@ -491,17 +503,66 @@ public class BubblePositioner {
int right = mIsLargeScreen
? mPositionRect.right - mExpandedViewLargeScreenInset + mExpandedViewPadding
: mPositionRect.right - mBubbleSize;
- x = onLeftEdge
+ x = state.onLeft
? left
: right;
} else {
y = mPositionRect.top + mExpandedViewPadding;
x = rowStart + positionInRow;
}
+
+ if (showBubblesVertically() && mImeVisible) {
+ return new PointF(x, getExpandedBubbleYForIme(index, state.numberOfBubbles));
+ }
return new PointF(x, y);
}
/**
+ * Returns the position of the bubble on-screen when the stack is expanded and the IME
+ * is showing.
+ *
+ * @param index the index of the bubble in the stack.
+ * @param numberOfBubbles the total number of bubbles in the stack.
+ * @return y position of the bubble on-screen when the stack is expanded.
+ */
+ private float getExpandedBubbleYForIme(int index, int numberOfBubbles) {
+ final float top = getAvailableRect().top + mExpandedViewPadding;
+ if (!showBubblesVertically()) {
+ // Showing horizontally: align to top
+ return top;
+ }
+
+ // Showing vertically: might need to translate the bubbles above the IME.
+ // Subtract spacing here to provide a margin between top of IME and bottom of bubble row.
+ final float bottomInset = getImeHeight() + mInsets.bottom - (mSpacingBetweenBubbles * 2);
+ final float expandedStackSize = getExpandedStackSize(numberOfBubbles);
+ final float centerPosition = showBubblesVertically()
+ ? mPositionRect.centerY()
+ : mPositionRect.centerX();
+ final float rowBottom = centerPosition + (expandedStackSize / 2f);
+ final float rowTop = centerPosition - (expandedStackSize / 2f);
+ float rowTopForIme = rowTop;
+ if (rowBottom > bottomInset) {
+ // We overlap with IME, must shift the bubbles
+ float translationY = rowBottom - bottomInset;
+ rowTopForIme = Math.max(rowTop - translationY, top);
+ if (rowTop - translationY < top) {
+ // Even if we shift the bubbles, they will still overlap with the IME.
+ // Hide the overflow for a lil more space:
+ final float expandedStackSizeNoO = getExpandedStackSize(numberOfBubbles - 1);
+ final float centerPositionNoO = showBubblesVertically()
+ ? mPositionRect.centerY()
+ : mPositionRect.centerX();
+ final float rowBottomNoO = centerPositionNoO + (expandedStackSizeNoO / 2f);
+ final float rowTopNoO = centerPositionNoO - (expandedStackSizeNoO / 2f);
+ translationY = rowBottomNoO - bottomInset;
+ rowTopForIme = rowTopNoO - translationY;
+ }
+ }
+ return rowTopForIme + (index * (mBubbleSize + mSpacingBetweenBubbles));
+ }
+
+ /**
* @return the width of the bubble flyout (message originating from the bubble).
*/
public float getMaxFlyoutSize() {
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 5bc6128d6c9e..9d0dd3c7fcdb 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
@@ -28,6 +28,8 @@ import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RES
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
@@ -188,6 +190,7 @@ public class BubbleStackView extends FrameLayout
};
private final BubbleController mBubbleController;
private final BubbleData mBubbleData;
+ private StackViewState mStackViewState = new StackViewState();
private final ValueAnimator mDismissBubbleAnimator;
@@ -246,7 +249,6 @@ public class BubbleStackView extends FrameLayout
private int mBubbleTouchPadding;
private int mExpandedViewPadding;
private int mCornerRadius;
- private int mImeOffset;
@Nullable private BubbleViewProvider mExpandedBubble;
private boolean mIsExpanded;
@@ -757,7 +759,6 @@ public class BubbleStackView extends FrameLayout
mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size);
mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding);
- mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
@@ -779,7 +780,7 @@ public class BubbleStackView extends FrameLayout
this::animateShadows /* onStackAnimationFinished */, mPositioner);
mExpandedAnimationController = new ExpandedAnimationController(mPositioner,
- onBubbleAnimatedOut);
+ onBubbleAnimatedOut, this);
mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
// Force LTR by default since most of the Bubbles UI is positioned manually by the user, or
@@ -890,7 +891,7 @@ public class BubbleStackView extends FrameLayout
// Re-draw bubble row and pointer for new orientation.
beforeExpandedViewAnimation();
updateOverflowVisibility();
- updatePointerPosition();
+ updatePointerPosition(false /* forIme */);
mExpandedAnimationController.expandFromStack(() -> {
afterExpandedViewAnimation();
} /* after */);
@@ -968,7 +969,8 @@ public class BubbleStackView extends FrameLayout
});
mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> {
if (mExpandedBubble != null) {
- mExpandedBubble.setExpandedContentAlpha((float) valueAnimator.getAnimatedValue());
+ mExpandedBubble.getExpandedView().setTaskViewAlpha(
+ (float) valueAnimator.getAnimatedValue());
}
});
@@ -1558,7 +1560,7 @@ public class BubbleStackView extends FrameLayout
} else {
bubble.cleanupViews();
}
- updatePointerPosition();
+ updatePointerPosition(false /* forIme */);
updateExpandedView();
logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
return;
@@ -1599,7 +1601,7 @@ public class BubbleStackView extends FrameLayout
.map(b -> b.getIconView()).collect(Collectors.toList());
mStackAnimationController.animateReorder(bubbleViews, reorder);
}
- updatePointerPosition();
+ updatePointerPosition(false /* forIme */);
}
/**
@@ -1670,7 +1672,6 @@ public class BubbleStackView extends FrameLayout
private void showNewlySelectedBubble(BubbleViewProvider bubbleToSelect) {
final BubbleViewProvider previouslySelected = mExpandedBubble;
mExpandedBubble = bubbleToSelect;
- updatePointerPosition();
if (mIsExpanded) {
hideCurrentInputMethod();
@@ -1762,6 +1763,7 @@ public class BubbleStackView extends FrameLayout
* not.
*/
void hideCurrentInputMethod() {
+ mPositioner.setImeVisible(false, 0);
mBubbleController.hideCurrentInputMethod();
}
@@ -1864,7 +1866,7 @@ public class BubbleStackView extends FrameLayout
updateBadges(false /* setBadgeForCollapsedStack */);
mBubbleContainer.setActiveController(mExpandedAnimationController);
updateOverflowVisibility();
- updatePointerPosition();
+ updatePointerPosition(false /* forIme */);
mExpandedAnimationController.expandFromStack(() -> {
if (mIsExpanded && mExpandedBubble.getExpandedView() != null) {
maybeShowManageEdu();
@@ -1876,8 +1878,7 @@ public class BubbleStackView extends FrameLayout
} else {
index = getBubbleIndex(mExpandedBubble);
}
- PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleContainer.getChildCount(),
- mStackOnLeftOrWillBe);
+ PointF p = mPositioner.getExpandedBubbleXY(index, getState());
final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
mPositioner.showBubblesVertically() ? p.y : p.x);
mExpandedViewContainer.setTranslationX(0f);
@@ -1928,7 +1929,7 @@ public class BubbleStackView extends FrameLayout
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
if (mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.setExpandedContentAlpha(0f);
+ mExpandedBubble.getExpandedView().setTaskViewAlpha(0f);
// We'll be starting the alpha animation after a slight delay, so set this flag early
// here.
@@ -2011,8 +2012,7 @@ public class BubbleStackView extends FrameLayout
index = mBubbleData.getBubbles().indexOf(mExpandedBubble);
}
// Value the bubble is animating from (back into the stack).
- final PointF p = mPositioner.getExpandedBubbleXY(index,
- mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
+ final PointF p = mPositioner.getExpandedBubbleXY(index, getState());
if (mPositioner.showBubblesVertically()) {
float pivotX;
float pivotY = p.y + mBubbleSize / 2f;
@@ -2109,7 +2109,7 @@ public class BubbleStackView extends FrameLayout
PointF p = mPositioner.getExpandedBubbleXY(isOverflow
? mBubbleContainer.getChildCount() - 1
: mBubbleData.getBubbles().indexOf(mExpandedBubble),
- mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
+ getState());
mExpandedViewContainer.setAlpha(1f);
mExpandedViewContainer.setVisibility(View.VISIBLE);
@@ -2187,9 +2187,20 @@ public class BubbleStackView extends FrameLayout
}
}
- /** Moves the bubbles out of the way if they're going to be over the keyboard. */
- public void onImeVisibilityChanged(boolean visible, int height) {
- mStackAnimationController.setImeHeight(visible ? height + mImeOffset : 0);
+ /**
+ * Updates the stack based for IME changes. When collapsed it'll move the stack if it
+ * overlaps where they IME would be. When expanded it'll shift the expanded bubbles
+ * if they might overlap with the IME (this only happens for large screens).
+ */
+ public void animateForIme(boolean visible) {
+ if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) {
+ // This will update the animation so the bubbles move to position for the IME
+ mExpandedAnimationController.expandFromStack(() -> {
+ updatePointerPosition(false /* forIme */);
+ afterExpandedViewAnimation();
+ } /* after */);
+ return;
+ }
if (!mIsExpanded && getBubbleCount() > 0) {
final float stackDestinationY =
@@ -2208,9 +2219,20 @@ public class BubbleStackView extends FrameLayout
FLYOUT_IME_ANIMATION_SPRING_CONFIG)
.start();
}
- } else if (mIsExpanded && mExpandedBubble != null
- && mExpandedBubble.getExpandedView() != null) {
+ } else if (mPositioner.showBubblesVertically() && mIsExpanded
+ && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
mExpandedBubble.getExpandedView().setImeVisible(visible);
+ List<Animator> animList = new ArrayList();
+ for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+ View child = mBubbleContainer.getChildAt(i);
+ float transY = mPositioner.getExpandedBubbleXY(i, getState()).y;
+ ObjectAnimator anim = ObjectAnimator.ofFloat(child, TRANSLATION_Y, transY);
+ animList.add(anim);
+ }
+ updatePointerPosition(true /* forIme */);
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(animList);
+ set.start();
}
}
@@ -2514,7 +2536,7 @@ public class BubbleStackView extends FrameLayout
// Account for the IME in the touchable region so that the touchable region of the
// Bubble window doesn't obscure the IME. The touchable region affects which areas
// of the screen can be excluded by lower windows (IME is just above the embedded task)
- outRect.bottom -= (int) mStackAnimationController.getImeHeight();
+ outRect.bottom -= mPositioner.getImeHeight();
}
if (mFlyout.getVisibility() == View.VISIBLE) {
@@ -2773,13 +2795,13 @@ public class BubbleStackView extends FrameLayout
}
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
PointF p = mPositioner.getExpandedBubbleXY(getBubbleIndex(mExpandedBubble),
- mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
+ getState());
mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble,
mPositioner.showBubblesVertically() ? p.y : p.x));
mExpandedViewContainer.setTranslationX(0f);
mExpandedBubble.getExpandedView().updateView(
mExpandedViewContainer.getLocationOnScreen());
- updatePointerPosition();
+ updatePointerPosition(false /* forIme */);
}
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
@@ -2850,7 +2872,13 @@ public class BubbleStackView extends FrameLayout
}
}
- private void updatePointerPosition() {
+ /**
+ * Updates the position of the pointer based on the expanded bubble.
+ *
+ * @param forIme whether the position is being updated due to the ime appearing, in this case
+ * the pointer is animated to the location.
+ */
+ private void updatePointerPosition(boolean forIme) {
if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
return;
}
@@ -2858,13 +2886,12 @@ public class BubbleStackView extends FrameLayout
if (index == -1) {
return;
}
- PointF bubblePosition = mPositioner.getExpandedBubbleXY(index,
- mBubbleContainer.getChildCount(),
- mStackOnLeftOrWillBe);
- mExpandedBubble.getExpandedView().setPointerPosition(mPositioner.showBubblesVertically()
- ? bubblePosition.y
- : bubblePosition.x,
- mStackOnLeftOrWillBe);
+ PointF position = mPositioner.getExpandedBubbleXY(index, getState());
+ float bubblePosition = mPositioner.showBubblesVertically()
+ ? position.y
+ : position.x;
+ mExpandedBubble.getExpandedView().setPointerPosition(bubblePosition,
+ mStackOnLeftOrWillBe, forIme /* animate */);
}
/**
@@ -2947,6 +2974,26 @@ public class BubbleStackView extends FrameLayout
return bubbles;
}
+ /** @return the current stack state. */
+ public StackViewState getState() {
+ mStackViewState.numberOfBubbles = mBubbleContainer.getChildCount();
+ mStackViewState.selectedIndex = getBubbleIndex(mExpandedBubble);
+ mStackViewState.onLeft = mStackOnLeftOrWillBe;
+ return mStackViewState;
+ }
+
+ /**
+ * Holds some commonly queried information about the stack.
+ */
+ public static class StackViewState {
+ // Number of bubbles (including the overflow itself) in the stack.
+ public int numberOfBubbles;
+ // The selected index if the stack is expanded.
+ public int selectedIndex;
+ // Whether the stack is resting on the left or right side of the screen when collapsed.
+ public boolean onLeft;
+ }
+
/**
* Representation of stack position that uses relative properties rather than absolute
* coordinates. This is used to maintain similar stack positions across configuration changes.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
index 38b3ba9dfda0..7e552826e94a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewProvider.java
@@ -29,12 +29,6 @@ public interface BubbleViewProvider {
@Nullable BubbleExpandedView getExpandedView();
/**
- * Sets the alpha of the expanded view content. This will be applied to both the expanded view
- * container itself (the manage button, etc.) as well as the TaskView within it.
- */
- void setExpandedContentAlpha(float alpha);
-
- /**
* Sets whether the contents of the bubble's TaskView should be visible.
*/
void setTaskViewVisibility(boolean visible);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index c32be98866cf..f0f78748e343 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -21,7 +21,6 @@ import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RES
import android.content.res.Resources;
import android.graphics.Path;
import android.graphics.PointF;
-import android.graphics.Rect;
import android.view.View;
import androidx.annotation.NonNull;
@@ -33,6 +32,7 @@ import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.animation.PhysicsAnimator;
import com.android.wm.shell.bubbles.BubblePositioner;
+import com.android.wm.shell.bubbles.BubbleStackView;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import com.google.android.collect.Sets;
@@ -124,12 +124,15 @@ public class ExpandedAnimationController
private BubblePositioner mPositioner;
+ private BubbleStackView mBubbleStackView;
+
public ExpandedAnimationController(BubblePositioner positioner,
- Runnable onBubbleAnimatedOutAction) {
+ Runnable onBubbleAnimatedOutAction, BubbleStackView stackView) {
mPositioner = positioner;
updateResources();
mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction;
mCollapsePoint = mPositioner.getDefaultStartPosition();
+ mBubbleStackView = stackView;
}
/**
@@ -239,10 +242,7 @@ public class ExpandedAnimationController
final Path path = new Path();
path.moveTo(bubble.getTranslationX(), bubble.getTranslationY());
- boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
- final PointF p = mPositioner.getExpandedBubbleXY(index,
- mLayout.getChildCount(),
- onLeft);
+ final PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleStackView.getState());
if (expanding) {
// If we're expanding, first draw a line from the bubble's current position to where
// it'll end up
@@ -364,7 +364,7 @@ public class ExpandedAnimationController
bubbleView.setTranslationY(y);
}
- final float expandedY = mPositioner.getExpandedBubblesY();
+ final float expandedY = mPositioner.getExpandedViewYTopAligned();
final boolean draggedOutEnough =
y > expandedY + mBubbleSizePx || y < expandedY - mBubbleSizePx;
if (draggedOutEnough != mBubbleDraggedOutEnough) {
@@ -410,8 +410,7 @@ public class ExpandedAnimationController
return;
}
final int index = mLayout.indexOfChild(bubbleView);
- final PointF p = mPositioner.getExpandedBubbleXY(index, mLayout.getChildCount(),
- mPositioner.isStackOnLeft(mCollapsePoint));
+ final PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleStackView.getState());
animationForChildAtIndex(index)
.position(p.x, p.y)
.withPositionStartVelocities(velX, velY)
@@ -429,16 +428,6 @@ public class ExpandedAnimationController
updateBubblePositions();
}
- /**
- * Animates the bubbles to the y position. Used in response to IME showing.
- */
- public void updateYPosition(Runnable after) {
- if (mLayout == null) return;
- animationsForChildrenFromIndex(
- 0, (i, anim) -> anim.translationY(mPositioner.getExpandedBubblesY()))
- .startAll(after);
- }
-
/** Description of current animation controller state. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("ExpandedAnimationController state:");
@@ -496,36 +485,33 @@ public class ExpandedAnimationController
startOrUpdatePathAnimation(false /* expanding */);
} else {
boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
- final PointF p = mPositioner.getExpandedBubbleXY(index,
- mLayout.getChildCount(),
- onLeft);
+ final PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleStackView.getState());
if (mPositioner.showBubblesVertically()) {
child.setTranslationY(p.y);
} else {
child.setTranslationX(p.x);
}
- if (!mPreparingToCollapse) {
- // Only animate if we're not collapsing as that animation will handle placing the
+
+ if (mPreparingToCollapse) {
+ // Don't animate if we're collapsing, as that animation will handle placing the
// new bubble in the stacked position.
- if (mPositioner.showBubblesVertically()) {
- Rect availableRect = mPositioner.getAvailableRect();
- float fromX = onLeft
- ? -mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR
- : availableRect.right + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
- animationForChild(child)
- .translationX(fromX, p.y)
- .start();
- } else {
- // Only animate if we're not collapsing as that animation will handle placing
- // the new bubble in the stacked position.
- float fromY = mPositioner.getExpandedBubblesY() - mBubbleSizePx
- * ANIMATE_TRANSLATION_FACTOR;
- animationForChild(child)
- .translationY(fromY, p.y)
- .start();
- }
- updateBubblePositions();
+ return;
}
+
+ if (mPositioner.showBubblesVertically()) {
+ float fromX = onLeft
+ ? p.x - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR
+ : p.x + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
+ animationForChild(child)
+ .translationX(fromX, p.y)
+ .start();
+ } else {
+ float fromY = p.y - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
+ animationForChild(child)
+ .translationY(fromY, p.y)
+ .start();
+ }
+ updateBubblePositions();
}
}
@@ -572,7 +558,6 @@ public class ExpandedAnimationController
if (mAnimatingExpand || mAnimatingCollapse) {
return;
}
- boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
for (int i = 0; i < mLayout.getChildCount(); i++) {
final View bubble = mLayout.getChildAt(i);
@@ -582,7 +567,7 @@ public class ExpandedAnimationController
return;
}
- final PointF p = mPositioner.getExpandedBubbleXY(i, mLayout.getChildCount(), onLeft);
+ final PointF p = mPositioner.getExpandedBubbleXY(i, mBubbleStackView.getState());
animationForChild(bubble)
.translationX(p.x)
.translationY(p.y)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 9a08190675b6..60b64333114e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -127,9 +127,6 @@ public class StackAnimationController extends
/** Whether or not the stack's start position has been set. */
private boolean mStackMovedToStartPosition = false;
- /** The height of the most recently visible IME. */
- private float mImeHeight = 0f;
-
/**
* The Y position of the stack before the IME became visible, or {@link Float#MIN_VALUE} if the
* IME is not visible or the user moved the stack since the IME became visible.
@@ -173,7 +170,7 @@ public class StackAnimationController extends
*/
private boolean mSpringToTouchOnNextMotionEvent = false;
- /** Horizontal offset of bubbles in the stack. */
+ /** Offset of bubbles in the stack (i.e. how much they overlap). */
private float mStackOffset;
/** Offset between stack y and animation y for bubble swap. */
private float mSwapAnimationOffset;
@@ -521,16 +518,6 @@ public class StackAnimationController extends
removeEndActionForProperty(DynamicAnimation.TRANSLATION_Y);
}
- /** Save the current IME height so that we know where the stack bounds should be. */
- public void setImeHeight(int imeHeight) {
- mImeHeight = imeHeight;
- }
-
- /** Returns the current IME height that the stack is offset by. */
- public float getImeHeight() {
- return mImeHeight;
- }
-
/**
* Animates the stack either away from the newly visible IME, or back to its original position
* due to the IME going away.
@@ -589,11 +576,14 @@ public class StackAnimationController extends
*/
public RectF getAllowableStackPositionRegion() {
final RectF allowableRegion = new RectF(mPositioner.getAvailableRect());
+ final int imeHeight = mPositioner.getImeHeight();
+ final float bottomPadding = getBubbleCount() > 1
+ ? mBubblePaddingTop + mStackOffset
+ : mBubblePaddingTop;
allowableRegion.left -= mBubbleOffscreen;
allowableRegion.top += mBubblePaddingTop;
allowableRegion.right += mBubbleOffscreen - mBubbleSize;
- allowableRegion.bottom -= mBubblePaddingTop + mBubbleSize
- + (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f);
+ allowableRegion.bottom -= imeHeight + bottomPadding + mBubbleSize;
return allowableRegion;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
new file mode 100644
index 000000000000..364bb651d55d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common.split;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
+import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
+import static android.view.RoundedCorner.POSITION_TOP_LEFT;
+import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.util.AttributeSet;
+import android.view.RoundedCorner;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+
+/**
+ * Draws inverted rounded corners beside divider bar to keep splitting tasks cropped with proper
+ * rounded corners.
+ */
+public class DividerRoundedCorner extends View {
+ private final int mDividerWidth;
+ private final Paint mDividerBarBackground;
+ private final Point mStartPos = new Point();
+ private InvertedRoundedCornerDrawInfo mTopLeftCorner;
+ private InvertedRoundedCornerDrawInfo mTopRightCorner;
+ private InvertedRoundedCornerDrawInfo mBottomLeftCorner;
+ private InvertedRoundedCornerDrawInfo mBottomRightCorner;
+
+ public DividerRoundedCorner(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ mDividerWidth = getResources().getDimensionPixelSize(R.dimen.split_divider_bar_width);
+ mDividerBarBackground = new Paint();
+ mDividerBarBackground.setColor(
+ getResources().getColor(R.color.split_divider_background, null));
+ mDividerBarBackground.setFlags(Paint.ANTI_ALIAS_FLAG);
+ mDividerBarBackground.setStyle(Paint.Style.FILL);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mTopLeftCorner = new InvertedRoundedCornerDrawInfo(POSITION_TOP_LEFT);
+ mTopRightCorner = new InvertedRoundedCornerDrawInfo(POSITION_TOP_RIGHT);
+ mBottomLeftCorner = new InvertedRoundedCornerDrawInfo(POSITION_BOTTOM_LEFT);
+ mBottomRightCorner = new InvertedRoundedCornerDrawInfo(POSITION_BOTTOM_RIGHT);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.save();
+
+ mTopLeftCorner.calculateStartPos(mStartPos);
+ canvas.translate(mStartPos.x, mStartPos.y);
+ canvas.drawPath(mTopLeftCorner.mPath, mDividerBarBackground);
+
+ canvas.translate(-mStartPos.x, -mStartPos.y);
+ mTopRightCorner.calculateStartPos(mStartPos);
+ canvas.translate(mStartPos.x, mStartPos.y);
+ canvas.drawPath(mTopRightCorner.mPath, mDividerBarBackground);
+
+ canvas.translate(-mStartPos.x, -mStartPos.y);
+ mBottomLeftCorner.calculateStartPos(mStartPos);
+ canvas.translate(mStartPos.x, mStartPos.y);
+ canvas.drawPath(mBottomLeftCorner.mPath, mDividerBarBackground);
+
+ canvas.translate(-mStartPos.x, -mStartPos.y);
+ mBottomRightCorner.calculateStartPos(mStartPos);
+ canvas.translate(mStartPos.x, mStartPos.y);
+ canvas.drawPath(mBottomRightCorner.mPath, mDividerBarBackground);
+
+ canvas.restore();
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
+ private boolean isLandscape() {
+ return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
+ }
+
+ /**
+ * Holds draw information of the inverted rounded corner at a specific position.
+ *
+ * @see {@link com.android.launcher3.taskbar.TaskbarDragLayer}
+ */
+ private class InvertedRoundedCornerDrawInfo {
+ @RoundedCorner.Position
+ private final int mCornerPosition;
+
+ private final int mRadius;
+
+ private final Path mPath = new Path();
+
+ InvertedRoundedCornerDrawInfo(@RoundedCorner.Position int cornerPosition) {
+ mCornerPosition = cornerPosition;
+
+ final RoundedCorner roundedCorner = getDisplay().getRoundedCorner(cornerPosition);
+ mRadius = roundedCorner == null ? 0 : roundedCorner.getRadius();
+
+ // Starts with a filled square, and then subtracting out a circle from the appropriate
+ // corner.
+ final Path square = new Path();
+ square.addRect(0, 0, mRadius, mRadius, Path.Direction.CW);
+ final Path circle = new Path();
+ circle.addCircle(
+ isLeftCorner() ? mRadius : 0 /* x */,
+ isTopCorner() ? mRadius : 0 /* y */,
+ mRadius, Path.Direction.CW);
+ mPath.op(square, circle, Path.Op.DIFFERENCE);
+ }
+
+ private void calculateStartPos(Point outPos) {
+ if (isLandscape()) {
+ // Place left corner at the right side of the divider bar.
+ outPos.x = isLeftCorner()
+ ? getWidth() / 2 + mDividerWidth / 2
+ : getWidth() / 2 - mDividerWidth / 2 - mRadius;
+ outPos.y = isTopCorner() ? 0 : getHeight() - mRadius;
+ } else {
+ outPos.x = isLeftCorner() ? 0 : getWidth() - mRadius;
+ // Place top corner at the bottom of the divider bar.
+ outPos.y = isTopCorner()
+ ? getHeight() / 2 + mDividerWidth / 2
+ : getHeight() / 2 - mDividerWidth / 2 - mRadius;
+ }
+ }
+
+ private boolean isLeftCorner() {
+ return mCornerPosition == POSITION_TOP_LEFT || mCornerPosition == POSITION_BOTTOM_LEFT;
+ }
+
+ private boolean isTopCorner() {
+ return mCornerPosition == POSITION_TOP_LEFT || mCornerPosition == POSITION_TOP_RIGHT;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index f35c8e1c5929..f9ba97f8f9b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -24,7 +24,6 @@ import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Property;
-import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.InsetsSource;
import android.view.InsetsState;
@@ -178,10 +177,12 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
return true;
}
+ // Convert to use screen-based coordinates to prevent lost track of motion events while
+ // moving divider bar and calculating dragging velocity.
+ event.setLocation(event.getRawX(), event.getRawY());
final int action = event.getAction() & MotionEvent.ACTION_MASK;
final boolean isLandscape = isLandscape();
- // Using raw xy to prevent lost track of motion events while moving divider bar.
- final int touchPos = isLandscape ? (int) event.getRawX() : (int) event.getRawY();
+ final int touchPos = (int) (isLandscape ? event.getX() : event.getY());
switch (action) {
case MotionEvent.ACTION_DOWN:
mVelocityTracker = VelocityTracker.obtain();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 754b8dadbfad..27c8d7ac0032 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -125,8 +125,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mRotation = configuration.windowConfiguration.getRotation();
mSplitLayoutHandler = splitLayoutHandler;
mDisplayImeController = displayImeController;
- mSplitWindowManager = new SplitWindowManager(
- windowName, mContext, configuration, parentContainerCallbacks);
+ mSplitWindowManager = new SplitWindowManager(windowName, mContext, configuration,
+ parentContainerCallbacks);
mTaskOrganizer = taskOrganizer;
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
mDismissingParallaxPolicy = new DismissingParallaxPolicy();
@@ -181,22 +181,19 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
public boolean updateConfiguration(Configuration configuration) {
boolean affectsLayout = false;
- // Make sure to render the divider bar with proper resources that matching the screen
- // orientation.
- final int orientation = configuration.orientation;
- if (orientation != mOrientation) {
- mOrientation = orientation;
- mContext = mContext.createConfigurationContext(configuration);
- mSplitWindowManager.setConfiguration(configuration);
- affectsLayout = true;
- }
-
// Update the split bounds when necessary. Besides root bounds changed, split bounds need to
// be updated when the rotation changed to cover the case that users rotated the screen 180
// degrees.
+ // Make sure to render the divider bar with proper resources that matching the screen
+ // orientation.
final int rotation = configuration.windowConfiguration.getRotation();
final Rect rootBounds = configuration.windowConfiguration.getBounds();
- if (rotation != mRotation || !mRootBounds.equals(rootBounds)) {
+ final int orientation = configuration.orientation;
+ if (rotation != mRotation || !mRootBounds.equals(rootBounds)
+ || orientation != mOrientation) {
+ mContext = mContext.createConfigurationContext(configuration);
+ mSplitWindowManager.setConfiguration(configuration);
+ mOrientation = orientation;
mTempRect.set(mRootBounds);
mRootBounds.set(rootBounds);
mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index fc7edfc4bceb..47dceb392183 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -64,6 +64,7 @@ public final class SplitWindowManager extends WindowlessWindowManager {
public interface ParentContainerCallbacks {
void attachToParentSurface(SurfaceControl.Builder b);
+ void onLeashReady(SurfaceControl leash);
}
public SplitWindowManager(String windowName, Context context, Configuration config,
@@ -100,6 +101,7 @@ public final class SplitWindowManager extends WindowlessWindowManager {
.setCallsite("SplitWindowManager#attachToParentSurface");
mParentContainerCallbacks.attachToParentSurface(builder);
mLeash = builder.build();
+ mParentContainerCallbacks.onLeashReady(mLeash);
b.setParent(mLeash);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 954ca14b4960..e511bffad247 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -445,6 +445,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
mOneHandedSettingsUtil.registerSettingsKeyObserver(
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
mContext.getContentResolver(), mShortcutEnabledObserver, newUserId);
+ mOneHandedSettingsUtil.registerSettingsKeyObserver(
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ mContext.getContentResolver(), mShortcutEnabledObserver, newUserId);
}
private void unregisterSettingObservers() {
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 d9708f05e7dd..493870d7fd72 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
@@ -18,8 +18,6 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -90,6 +88,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+import com.android.wm.shell.common.split.SplitWindowManager;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.Transitions;
@@ -163,6 +162,19 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mDismissTop = NO_DISMISS;
};
+ private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
+ new SplitWindowManager.ParentContainerCallbacks() {
+ @Override
+ public void attachToParentSurface(SurfaceControl.Builder b) {
+ mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b);
+ }
+
+ @Override
+ public void onLeashReady(SurfaceControl leash) {
+ mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+ }
+ };
+
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
DisplayImeController displayImeController,
@@ -501,7 +513,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
mMainStage.deactivate(wct, childrenToTop == mMainStage);
mTaskOrganizer.applyTransaction(wct);
- // Reset divider position.
+ // Hide divider and reset its position.
+ setDividerVisibility(false);
mSplitLayout.resetDividerPosition();
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
if (childrenToTop != null) {
@@ -635,10 +648,16 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void onStageVisibilityChanged(StageListenerImpl stageListener) {
final boolean sideStageVisible = mSideStageListener.mVisible;
final boolean mainStageVisible = mMainStageListener.mVisible;
- // Divider is only visible if both the main stage and side stages are visible
- setDividerVisibility(isSplitScreenVisible());
+ final boolean bothStageVisible = sideStageVisible && mainStageVisible;
+ final boolean bothStageInvisible = !sideStageVisible && !mainStageVisible;
+ final boolean sameVisibility = sideStageVisible == mainStageVisible;
+ // Only add or remove divider when both visible or both invisible to avoid sometimes we only
+ // got one stage visibility changed for a moment and it will cause flicker.
+ if (sameVisibility) {
+ setDividerVisibility(bothStageVisible);
+ }
- if (!mainStageVisible && !sideStageVisible) {
+ if (bothStageInvisible) {
if (mExitSplitScreenOnHide
// Don't dismiss staged split when both stages are not visible due to sleeping display,
// like the cases keyguard showing or screen off.
@@ -655,59 +674,32 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
exitSplitScreen(toTop, SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP);
}
- // When both stage's visibility changed to visible, main stage might receives visibility
- // changed before side stage if it has higher z-order than side stage. Make sure we only
- // update main stage's windowing mode with the visibility changed of side stage to prevent
- // stacking multiple windowing mode transactions which result to flicker issue.
- if (mainStageVisible && stageListener == mSideStageListener) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- if (sideStageVisible) {
- // The main stage configuration should to follow split layout when side stage is
- // visible.
- mMainStage.updateConfiguration(
- WINDOWING_MODE_MULTI_WINDOW, getMainStageBounds(), wct);
- } else if (!mSideStage.mRootTaskInfo.isSleeping) {
- // We want the main stage configuration to be fullscreen when the side stage isn't
- // visible.
- // We should not do it when side stage are not visible due to sleeping display too.
- mMainStage.updateConfiguration(WINDOWING_MODE_FULLSCREEN, null, wct);
- }
- // TODO: Change to `mSyncQueue.queue(wct)` once BLAST is stable.
- mTaskOrganizer.applyTransaction(wct);
- }
-
mSyncQueue.runInSync(t -> {
final SurfaceControl sideStageLeash = mSideStage.mRootLeash;
final SurfaceControl mainStageLeash = mMainStage.mRootLeash;
if (sideStageVisible) {
final Rect sideStageBounds = getSideStageBounds();
- t.show(sideStageLeash)
- .setPosition(sideStageLeash,
- sideStageBounds.left, sideStageBounds.top)
+ t.setPosition(sideStageLeash,
+ sideStageBounds.left, sideStageBounds.top)
.setWindowCrop(sideStageLeash,
sideStageBounds.width(), sideStageBounds.height());
- } else {
- t.hide(sideStageLeash);
}
if (mainStageVisible) {
final Rect mainStageBounds = getMainStageBounds();
- t.show(mainStageLeash);
- if (sideStageVisible) {
- t.setPosition(mainStageLeash, mainStageBounds.left, mainStageBounds.top)
- .setWindowCrop(mainStageLeash,
- mainStageBounds.width(), mainStageBounds.height());
- } else {
- // Clear window crop and position if side stage isn't visible.
- t.setPosition(mainStageLeash, 0, 0)
- .setWindowCrop(mainStageLeash, null);
- }
- } else {
- t.hide(mainStageLeash);
+ t.setPosition(mainStageLeash, mainStageBounds.left, mainStageBounds.top)
+ .setWindowCrop(mainStageLeash,
+ mainStageBounds.width(), mainStageBounds.height());
}
- applyDividerVisibility(t);
+ // Same above, we only set root tasks and divider leash visibility when both stage
+ // change to visible or invisible to avoid flicker.
+ if (sameVisibility) {
+ t.setVisibility(sideStageLeash, bothStageVisible)
+ .setVisibility(mainStageLeash, bothStageVisible);
+ applyDividerVisibility(t);
+ }
});
}
@@ -726,7 +718,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
} else {
t.hide(dividerLeash);
}
-
}
private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
@@ -852,8 +843,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mDisplayAreaInfo = displayAreaInfo;
if (mSplitLayout == null) {
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
- mDisplayAreaInfo.configuration, this,
- b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b),
+ mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
mDisplayImeController, mTaskOrganizer);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
}
@@ -871,7 +861,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
&& mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)
&& mMainStage.isActive()) {
onLayoutChanged(mSplitLayout);
- mSyncQueue.runInSync(t -> applyDividerVisibility(t));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index debe6d56dd92..ff3428cd9a6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -559,6 +559,15 @@ public class StartingSurfaceDrawer {
removeWindowSynced(taskId, null, null, false);
}
+ void onImeDrawnOnTask(int taskId) {
+ final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
+ if (record != null && record.mTaskSnapshotWindow != null
+ && record.mTaskSnapshotWindow.hasImeSurface()) {
+ record.mTaskSnapshotWindow.removeImmediately();
+ }
+ mStartingWindowRecords.remove(taskId);
+ }
+
protected void removeWindowSynced(int taskId, SurfaceControl leash, Rect frame,
boolean playRevealAnimation) {
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
@@ -586,14 +595,15 @@ public class StartingSurfaceDrawer {
Slog.e(TAG, "Found empty splash screen, remove!");
removeWindowInner(record.mDecorView, false);
}
+ mStartingWindowRecords.remove(taskId);
}
if (record.mTaskSnapshotWindow != null) {
if (DEBUG_TASK_SNAPSHOT) {
Slog.v(TAG, "Removing task snapshot window for " + taskId);
}
- record.mTaskSnapshotWindow.remove();
+ record.mTaskSnapshotWindow.scheduleRemove(
+ () -> mStartingWindowRecords.remove(taskId));
}
- mStartingWindowRecords.remove(taskId);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index a5c47c41180e..4433e275cd1b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -134,7 +134,7 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken,
suggestionType);
} else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {
- final TaskSnapshot snapshot = windowInfo.mTaskSnapshot;
+ final TaskSnapshot snapshot = windowInfo.taskSnapshot;
mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken,
snapshot);
}
@@ -177,6 +177,13 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
}
/**
+ * Called when the IME has drawn on the organized task.
+ */
+ public void onImeDrawnOnTask(int taskId) {
+ mSplashScreenExecutor.execute(() -> mStartingSurfaceDrawer.onImeDrawnOnTask(taskId));
+ }
+
+ /**
* Called when the content of a task is ready to show, starting window can be removed.
*/
public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index cdc0795e0331..3538fd2d84a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.startingsurface;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.graphics.Color.WHITE;
import static android.graphics.Color.alpha;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -64,7 +63,6 @@ import android.graphics.RectF;
import android.hardware.HardwareBuffer;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.Trace;
import android.util.MergedConfiguration;
import android.util.Slog;
@@ -72,7 +70,6 @@ import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.InsetsVisibilities;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
@@ -120,7 +117,12 @@ public class TaskSnapshotWindow {
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
private static final long DELAY_REMOVAL_TIME_GENERAL = 100;
- private static final long DELAY_REMOVAL_TIME_IME_VISIBLE = 350;
+ /**
+ * The max delay time in milliseconds for removing the task snapshot window with IME visible.
+ * Ideally the delay time will be shorter when receiving
+ * {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}.
+ */
+ private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 450;
//tmp vars for unused relayout params
private static final Point TMP_SURFACE_SIZE = new Point();
@@ -139,7 +141,6 @@ public class TaskSnapshotWindow {
private final RectF mTmpDstFrame = new RectF();
private final CharSequence mTitle;
private boolean mHasDrawn;
- private long mShownTime;
private boolean mSizeMismatch;
private final Paint mBackgroundPaint = new Paint();
private final int mActivityType;
@@ -149,6 +150,8 @@ public class TaskSnapshotWindow {
private final SurfaceControl.Transaction mTransaction;
private final Matrix mSnapshotMatrix = new Matrix();
private final float[] mTmpFloat9 = new float[9];
+ private Runnable mScheduledRunnable;
+ private final boolean mHasImeSurface;
static TaskSnapshotWindow create(StartingWindowInfo info, IBinder appToken,
TaskSnapshot snapshot, ShellExecutor splashScreenExecutor,
@@ -217,7 +220,7 @@ public class TaskSnapshotWindow {
taskDescription.setBackgroundColor(WHITE);
}
- final long delayRemovalTime = snapshot.hasImeSurface() ? DELAY_REMOVAL_TIME_IME_VISIBLE
+ final long delayRemovalTime = snapshot.hasImeSurface() ? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE
: DELAY_REMOVAL_TIME_GENERAL;
final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
@@ -227,13 +230,12 @@ public class TaskSnapshotWindow {
final Window window = snapshotSurface.mWindow;
final InsetsState tmpInsetsState = new InsetsState();
- final InsetsVisibilities tmpRequestedVisibilities = new InsetsVisibilities();
final InputChannel tmpInputChannel = new InputChannel();
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#addToDisplay");
final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
- tmpRequestedVisibilities, tmpInputChannel, tmpInsetsState, tmpControls);
+ info.requestedVisibilities, tmpInputChannel, tmpInsetsState, tmpControls);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
@@ -283,12 +285,17 @@ public class TaskSnapshotWindow {
mDelayRemovalTime = delayRemovalTime;
mTransaction = new SurfaceControl.Transaction();
mClearWindowHandler = clearWindowHandler;
+ mHasImeSurface = snapshot.hasImeSurface();
}
int getBackgroundColor() {
return mBackgroundPaint.getColor();
}
+ boolean hasImeSurface() {
+ return mHasImeSurface;
+ }
+
/**
* Ask system bar background painter to draw status bar background.
* @hide
@@ -306,21 +313,26 @@ public class TaskSnapshotWindow {
mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
}
- void remove() {
- final long now = SystemClock.uptimeMillis();
- if ((now - mShownTime < mDelayRemovalTime)
- // Show the latest content as soon as possible for unlocking to home.
- && mActivityType != ACTIVITY_TYPE_HOME) {
- final long delayTime = mShownTime + mDelayRemovalTime - now;
- mSplashScreenExecutor.executeDelayed(() -> remove(), delayTime);
- if (DEBUG) {
- Slog.d(TAG, "Defer removing snapshot surface in " + delayTime);
- }
- return;
+ void scheduleRemove(Runnable onRemove) {
+ if (mScheduledRunnable != null) {
+ mSplashScreenExecutor.removeCallbacks(mScheduledRunnable);
+ mScheduledRunnable = null;
+ }
+ mScheduledRunnable = () -> {
+ TaskSnapshotWindow.this.removeImmediately();
+ onRemove.run();
+ };
+ mSplashScreenExecutor.executeDelayed(mScheduledRunnable, mDelayRemovalTime);
+ if (DEBUG) {
+ Slog.d(TAG, "Defer removing snapshot surface in " + mDelayRemovalTime);
}
+ }
+
+ void removeImmediately() {
+ mSplashScreenExecutor.removeCallbacks(mScheduledRunnable);
try {
if (DEBUG) {
- Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn);
+ Slog.d(TAG, "Removing taskSnapshot surface, mHasDrawn: " + mHasDrawn);
}
mSession.remove(mWindow);
} catch (RemoteException e) {
@@ -358,7 +370,6 @@ public class TaskSnapshotWindow {
} else {
drawSizeMatchSnapshot();
}
- mShownTime = SystemClock.uptimeMillis();
mHasDrawn = true;
reportDrawn();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
index 848eff4b56f3..e6d6028b2aba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java
@@ -107,7 +107,7 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
* rotation must be the same).
*/
private boolean isSnapshotCompatible(StartingWindowInfo windowInfo) {
- final TaskSnapshot snapshot = windowInfo.mTaskSnapshot;
+ final TaskSnapshot snapshot = windowInfo.taskSnapshot;
if (snapshot == null) {
if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
Slog.d(TAG, "isSnapshotCompatible no snapshot " + windowInfo.taskInfo.taskId);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
index 9732a8890e0e..2b9bdce45a6c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -16,10 +16,12 @@
package com.android.wm.shell.bubbles.animation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static org.mockito.Mockito.when;
import android.annotation.SuppressLint;
import android.content.res.Configuration;
@@ -37,6 +39,7 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.BubblePositioner;
+import com.android.wm.shell.bubbles.BubbleStackView;
import org.junit.Before;
import org.junit.Ignore;
@@ -56,18 +59,22 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
private int mStackOffset;
private PointF mExpansionPoint;
private BubblePositioner mPositioner;
+ private BubbleStackView.StackViewState mStackViewState;
@SuppressLint("VisibleForTests")
@Before
public void setUp() throws Exception {
super.setUp();
+ BubbleStackView stackView = mock(BubbleStackView.class);
+ when(stackView.getState()).thenReturn(getStackViewState());
mPositioner = new BubblePositioner(getContext(), mock(WindowManager.class));
mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
Insets.of(0, 0, 0, 0),
new Rect(0, 0, mDisplayWidth, mDisplayHeight));
mExpandedController = new ExpandedAnimationController(mPositioner,
- mOnBubbleAnimatedOutAction);
+ mOnBubbleAnimatedOutAction,
+ stackView);
spyOn(mExpandedController);
addOneMoreThanBubbleLimitBubbles();
@@ -78,6 +85,13 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
mExpansionPoint = new PointF(100, 100);
}
+ public BubbleStackView.StackViewState getStackViewState() {
+ mStackViewState.numberOfBubbles = mLayout.getChildCount();
+ mStackViewState.selectedIndex = 0;
+ mStackViewState.onLeft = mPositioner.isStackOnLeft(mExpansionPoint);
+ return mStackViewState;
+ }
+
@Test
@Ignore
public void testExpansionAndCollapse() throws InterruptedException {
@@ -141,12 +155,10 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
/** Check that children are in the correct positions for being expanded. */
private void testBubblesInCorrectExpandedPositions() {
- boolean onLeft = mPositioner.isStackOnLeft(mExpansionPoint);
// Check all the visible bubbles to see if they're in the right place.
for (int i = 0; i < mLayout.getChildCount(); i++) {
PointF expectedPosition = mPositioner.getExpandedBubbleXY(i,
- mLayout.getChildCount(),
- onLeft);
+ getStackViewState());
assertEquals(expectedPosition.x,
mLayout.getChildAt(i).getTranslationX(),
2f);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 3557906531b2..defa58d7fe93 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -29,7 +29,6 @@ import static org.mockito.Mockito.verify;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.view.SurfaceControl;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -53,7 +52,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class SplitLayoutTests extends ShellTestCase {
@Mock SplitLayout.SplitLayoutHandler mSplitLayoutHandler;
- @Mock SurfaceControl mRootLeash;
+ @Mock SplitWindowManager.ParentContainerCallbacks mCallbacks;
@Mock DisplayImeController mDisplayImeController;
@Mock ShellTaskOrganizer mTaskOrganizer;
@Captor ArgumentCaptor<Runnable> mRunnableCaptor;
@@ -67,7 +66,7 @@ public class SplitLayoutTests extends ShellTestCase {
mContext,
getConfiguration(),
mSplitLayoutHandler,
- b -> b.setParent(mRootLeash),
+ mCallbacks,
mDisplayImeController,
mTaskOrganizer));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
index c456c7de8821..9bb54a18063f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java
@@ -23,7 +23,6 @@ import static org.mockito.Mockito.when;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.view.InsetsState;
-import android.view.SurfaceControl;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -41,8 +40,8 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SplitWindowManagerTests extends ShellTestCase {
- @Mock SurfaceControl mSurfaceControl;
@Mock SplitLayout mSplitLayout;
+ @Mock SplitWindowManager.ParentContainerCallbacks mCallbacks;
private SplitWindowManager mSplitWindowManager;
@Before
@@ -51,7 +50,7 @@ public class SplitWindowManagerTests extends ShellTestCase {
final Configuration configuration = new Configuration();
configuration.setToDefaults();
mSplitWindowManager = new SplitWindowManager("TestSplitDivider", mContext, configuration,
- b -> b.setParent(mSurfaceControl));
+ mCallbacks);
when(mSplitLayout.getDividerBounds()).thenReturn(
new Rect(0, 0, configuration.windowConfiguration.getBounds().width(),
configuration.windowConfiguration.getBounds().height()));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 2994e7181369..36722d9147ab 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -15,28 +15,38 @@
*/
package com.android.wm.shell.startingsurface;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.graphics.ColorSpace;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
@@ -44,11 +54,16 @@ import android.os.Looper;
import android.os.UserHandle;
import android.testing.TestableContext;
import android.view.Display;
+import android.view.IWindowSession;
+import android.view.InsetsState;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.WindowMetrics;
import android.window.StartingWindowInfo;
+import android.window.TaskSnapshot;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -63,6 +78,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
import java.util.function.IntSupplier;
@@ -80,6 +96,7 @@ public class StartingSurfaceDrawerTests {
private TransactionPool mTransactionPool;
private final Handler mTestHandler = new Handler(Looper.getMainLooper());
+ private ShellExecutor mTestExecutor;
private final TestableContext mTestContext = new TestContext(
InstrumentationRegistry.getInstrumentation().getTargetContext());
TestStartingSurfaceDrawer mStartingSurfaceDrawer;
@@ -138,9 +155,9 @@ public class StartingSurfaceDrawerTests {
doReturn(metrics).when(mMockWindowManager).getMaximumWindowMetrics();
doNothing().when(mMockWindowManager).addView(any(), any());
-
- mStartingSurfaceDrawer = spy(new TestStartingSurfaceDrawer(mTestContext,
- new HandlerExecutor(mTestHandler), mTransactionPool));
+ mTestExecutor = new HandlerExecutor(mTestHandler);
+ mStartingSurfaceDrawer = spy(
+ new TestStartingSurfaceDrawer(mTestContext, mTestExecutor, mTransactionPool));
}
@Test
@@ -208,6 +225,48 @@ public class StartingSurfaceDrawerTests {
assertEquals(0, windowColor3.mReuseCount);
}
+ @Test
+ public void testRemoveTaskSnapshotWithImeSurfaceWhenOnImeDrawn() throws Exception {
+ final int taskId = 1;
+ final StartingWindowInfo windowInfo =
+ createWindowInfo(taskId, android.R.style.Theme);
+ TaskSnapshot snapshot = createTaskSnapshot(100, 100, new Point(100, 100),
+ new Rect(0, 0, 0, 50), true /* hasImeSurface */);
+ final IWindowSession session = WindowManagerGlobal.getWindowSession();
+ spyOn(session);
+ doReturn(WindowManagerGlobal.ADD_OKAY).when(session).addToDisplay(
+ any() /* window */, any() /* attrs */,
+ anyInt() /* viewVisibility */, anyInt() /* displayId */,
+ any() /* requestedVisibility */, any() /* outInputChannel */,
+ any() /* outInsetsState */, any() /* outActiveControls */);
+ TaskSnapshotWindow mockSnapshotWindow = TaskSnapshotWindow.create(windowInfo,
+ mBinder,
+ snapshot, mTestExecutor, () -> {
+ });
+ spyOn(mockSnapshotWindow);
+ try (AutoCloseable mockTaskSnapshotSession = new AutoCloseable() {
+ MockitoSession mockSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(TaskSnapshotWindow.class)
+ .startMocking();
+ @Override
+ public void close() {
+ mockSession.finishMocking();
+ }
+ }) {
+ when(TaskSnapshotWindow.create(eq(windowInfo), eq(mBinder), eq(snapshot), any(),
+ any())).thenReturn(mockSnapshotWindow);
+ // Simulate a task snapshot window created with IME snapshot shown.
+ mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, mBinder, snapshot);
+ waitHandlerIdle(mTestHandler);
+
+ // Verify the task snapshot with IME snapshot will be removed when received the real IME
+ // drawn callback.
+ mStartingSurfaceDrawer.onImeDrawnOnTask(1);
+ verify(mockSnapshotWindow).removeImmediately();
+ }
+ }
+
private StartingWindowInfo createWindowInfo(int taskId, int themeResId) {
StartingWindowInfo windowInfo = new StartingWindowInfo();
final ActivityInfo info = new ActivityInfo();
@@ -219,10 +278,27 @@ public class StartingSurfaceDrawerTests {
taskInfo.taskId = taskId;
windowInfo.targetActivityInfo = info;
windowInfo.taskInfo = taskInfo;
+ windowInfo.topOpaqueWindowInsetsState = new InsetsState();
+ windowInfo.mainWindowLayoutParams = new WindowManager.LayoutParams();
+ windowInfo.topOpaqueWindowLayoutParams = new WindowManager.LayoutParams();
return windowInfo;
}
private static void waitHandlerIdle(Handler handler) {
handler.runWithScissors(() -> { }, 0 /* timeout */);
}
+
+ private TaskSnapshot createTaskSnapshot(int width, int height, Point taskSize,
+ Rect contentInsets, boolean hasImeSurface) {
+ final HardwareBuffer buffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888,
+ 1, HardwareBuffer.USAGE_CPU_READ_RARELY);
+ return new TaskSnapshot(
+ System.currentTimeMillis(),
+ new ComponentName("", ""), buffer,
+ ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
+ Surface.ROTATION_0, taskSize, contentInsets, false,
+ true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
+ 0 /* systemUiVisibility */, false /* isTranslucent */,
+ hasImeSurface /* hasImeSurface */);
+ }
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 5db9ddfac870..38f9607c9529 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2443,17 +2443,11 @@ public class AudioManager {
/**
* Return a handle to the optional platform's {@link Spatializer}
- * @return {@code null} if spatialization is not supported, the {@code Spatializer} instance
- * otherwise.
+ * @return the {@code Spatializer} instance.
+ * @see Spatializer#getImmersiveAudioLevel() to check for the level of support of the effect
+ * on the platform
*/
- public @Nullable Spatializer getSpatializer() {
- int level = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
- try {
- level = getService().getSpatializerImmersiveAudioLevel();
- } catch (Exception e) { /* using NONE */ }
- if (level == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE) {
- return null;
- }
+ public @NonNull Spatializer getSpatializer() {
return new Spatializer(this);
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 8480c5289d15..bc1040115417 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -36,6 +36,8 @@ import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.ISpatializerCallback;
+import android.media.ISpatializerHeadTrackingModeCallback;
+import android.media.ISpatializerHeadToSoundStagePoseCallback;
import android.media.IVolumeController;
import android.media.IVolumeController;
import android.media.PlayerBase;
@@ -405,13 +407,33 @@ interface IAudioService {
boolean canBeSpatialized(in AudioAttributes aa, in AudioFormat af);
- void registerSpatializerCallback(in ISpatializerCallback callback);
+ void registerSpatializerCallback(in ISpatializerCallback cb);
- void unregisterSpatializerCallback(in ISpatializerCallback callback);
+ void unregisterSpatializerCallback(in ISpatializerCallback cb);
+
+ void registerSpatializerHeadTrackingCallback(in ISpatializerHeadTrackingModeCallback cb);
+
+ void unregisterSpatializerHeadTrackingCallback(in ISpatializerHeadTrackingModeCallback cb);
+
+ void registerHeadToSoundstagePoseCallback(in ISpatializerHeadToSoundStagePoseCallback cb);
+
+ void unregisterHeadToSoundstagePoseCallback(in ISpatializerHeadToSoundStagePoseCallback cb);
List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices();
void addSpatializerCompatibleAudioDevice(in AudioDeviceAttributes ada);
void removeSpatializerCompatibleAudioDevice(in AudioDeviceAttributes ada);
+
+ void setDesiredHeadTrackingMode(int mode);
+
+ int getDesiredHeadTrackingMode();
+
+ int[] getSupportedHeadTrackingModes();
+
+ int getActualHeadTrackingMode();
+
+ oneway void setSpatializerGlobalTransform(in float[] transform);
+
+ oneway void recenterHeadTracker();
}
diff --git a/media/java/android/media/ISpatializerHeadToSoundStagePoseCallback.aidl b/media/java/android/media/ISpatializerHeadToSoundStagePoseCallback.aidl
new file mode 100644
index 000000000000..01a146599394
--- /dev/null
+++ b/media/java/android/media/ISpatializerHeadToSoundStagePoseCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+/**
+ * AIDL for the AudioService to signal Spatializer state changes.
+ *
+ * {@hide}
+ */
+oneway interface ISpatializerHeadToSoundStagePoseCallback {
+
+ /**
+ * The pose is sent as an array of 6 float values, the first 3 are the translation vector, the
+ * other 3 are the rotation vector.
+ */
+ void dispatchPoseChanged(in float[] pose);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.java b/media/java/android/media/ISpatializerHeadTrackingModeCallback.aidl
index b36b67dc02c0..c61f86e4c60e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.java
+++ b/media/java/android/media/ISpatializerHeadTrackingModeCallback.aidl
@@ -14,26 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone;
+package android.media;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.util.ViewController;
-
-/** Controller for {@link PhoneStatusBarView}. */
-public class PhoneStatusBarViewController extends ViewController<PhoneStatusBarView> {
-
- protected PhoneStatusBarViewController(
- PhoneStatusBarView view,
- CommandQueue commandQueue) {
- super(view);
- mView.setPanelEnabledProvider(commandQueue::panelsEnabled);
- }
+/**
+ * AIDL for the AudioService to signal Spatializer head tracking mode changes.
+ *
+ * {@hide}
+ */
+oneway interface ISpatializerHeadTrackingModeCallback {
- @Override
- protected void onViewAttached() {
- }
+ void dispatchSpatializerActualHeadTrackingModeChanged(int mode);
- @Override
- protected void onViewDetached() {
- }
+ void dispatchSpatializerDesiredHeadTrackingModeChanged(int mode);
}
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
index 3ed8b58959a1..b062eea5017c 100644
--- a/media/java/android/media/Spatializer.java
+++ b/media/java/android/media/Spatializer.java
@@ -49,21 +49,9 @@ public class Spatializer {
private final @NonNull AudioManager mAm;
- private final Object mStateListenerLock = new Object();
-
private static final String TAG = "Spatializer";
/**
- * List of listeners for state listener and their associated Executor.
- * List is lazy-initialized on first registration
- */
- @GuardedBy("mStateListenerLock")
- private @Nullable ArrayList<StateListenerInfo> mStateListeners;
-
- @GuardedBy("mStateListenerLock")
- private SpatializerInfoDispatcherStub mInfoDispatcherStub;
-
- /**
* @hide
* Constructor with AudioManager acting as proxy to AudioService
* @param am a non-null AudioManager
@@ -114,6 +102,7 @@ public class Spatializer {
/** @hide */
@IntDef(flag = false, value = {
+ SPATIALIZER_IMMERSIVE_LEVEL_OTHER,
SPATIALIZER_IMMERSIVE_LEVEL_NONE,
SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL,
})
@@ -121,20 +110,110 @@ public class Spatializer {
public @interface ImmersiveAudioLevel {};
/**
- * @hide
+ * Constant indicating the {@code Spatializer} on this device supports a spatialization
+ * mode that differs from the ones available at this SDK level.
+ * @see #getImmersiveAudioLevel()
+ */
+ public static final int SPATIALIZER_IMMERSIVE_LEVEL_OTHER = -1;
+
+ /**
* Constant indicating there are no spatialization capabilities supported on this device.
- * @see AudioManager#getImmersiveAudioLevel()
+ * @see #getImmersiveAudioLevel()
*/
public static final int SPATIALIZER_IMMERSIVE_LEVEL_NONE = 0;
/**
- * @hide
- * Constant indicating the {@link Spatializer} on this device supports multichannel
+ * Constant indicating the {@code Spatializer} on this device supports multichannel
* spatialization.
- * @see AudioManager#getImmersiveAudioLevel()
+ * @see #getImmersiveAudioLevel()
*/
public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1;
+ /** @hide */
+ @IntDef(flag = false, value = {
+ HEAD_TRACKING_MODE_UNSUPPORTED,
+ HEAD_TRACKING_MODE_DISABLED,
+ HEAD_TRACKING_MODE_RELATIVE_WORLD,
+ HEAD_TRACKING_MODE_RELATIVE_DEVICE,
+ }) public @interface HeadTrackingMode {};
+
+ /** @hide */
+ @IntDef(flag = false, value = {
+ HEAD_TRACKING_MODE_DISABLED,
+ HEAD_TRACKING_MODE_RELATIVE_WORLD,
+ HEAD_TRACKING_MODE_RELATIVE_DEVICE,
+ }) public @interface HeadTrackingModeSet {};
+
+ /** @hide */
+ @IntDef(flag = false, value = {
+ HEAD_TRACKING_MODE_RELATIVE_WORLD,
+ HEAD_TRACKING_MODE_RELATIVE_DEVICE,
+ }) public @interface HeadTrackingModeSupported {};
+
+ /**
+ * @hide
+ * Constant indicating head tracking is not supported by this {@code Spatializer}
+ * @see #getHeadTrackingMode()
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public static final int HEAD_TRACKING_MODE_UNSUPPORTED = -2;
+
+ /**
+ * @hide
+ * Constant indicating head tracking is disabled on this {@code Spatializer}
+ * @see #getHeadTrackingMode()
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public static final int HEAD_TRACKING_MODE_DISABLED = -1;
+
+ /**
+ * @hide
+ * Constant indicating head tracking is in a mode whose behavior is unknown. This is not an
+ * error state but represents a customized behavior not defined by this API.
+ * @see #getHeadTrackingMode()
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public static final int HEAD_TRACKING_MODE_OTHER = 0;
+
+ /**
+ * @hide
+ * Constant indicating head tracking is tracking the user's position / orientation relative to
+ * the world around them
+ * @see #getHeadTrackingMode()
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public static final int HEAD_TRACKING_MODE_RELATIVE_WORLD = 1;
+
+ /**
+ * @hide
+ * Constant indicating head tracking is tracking the user's position / orientation relative to
+ * the device
+ * @see #getHeadTrackingMode()
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public static final int HEAD_TRACKING_MODE_RELATIVE_DEVICE = 2;
+
+ /**
+ * Return the level of support for the spatialization feature on this device.
+ * This level of support is independent of whether the {@code Spatializer} is currently
+ * enabled or available and will not change over time.
+ * @return the level of spatialization support
+ * @see #isEnabled()
+ * @see #isAvailable()
+ */
+ public @ImmersiveAudioLevel int getImmersiveAudioLevel() {
+ int level = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ try {
+ level = mAm.getService().getSpatializerImmersiveAudioLevel();
+ } catch (Exception e) { /* using NONE */ }
+ return level;
+ }
+
/**
* @hide
* Enables / disables the spatializer effect.
@@ -154,7 +233,7 @@ public class Spatializer {
}
/**
- * An interface to be notified of changes to the state of the spatializer.
+ * An interface to be notified of changes to the state of the spatializer effect.
*/
public interface OnSpatializerStateChangedListener {
/**
@@ -178,6 +257,58 @@ public class Spatializer {
}
/**
+ * @hide
+ * An interface to be notified of changes to the head tracking mode, used by the spatializer
+ * effect.
+ * Changes to the mode may come from explicitly setting a different mode
+ * (see {@link #setDesiredHeadTrackingMode(int)}) or a change in system conditions (see
+ * {@link #getHeadTrackingMode()}
+ * @see #addOnHeadTrackingModeChangedListener(Executor, OnHeadTrackingModeChangedListener)
+ * @see #removeOnHeadTrackingModeChangedListener(OnHeadTrackingModeChangedListener)
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ public interface OnHeadTrackingModeChangedListener {
+ /**
+ * Called when the actual head tracking mode of the spatializer changed.
+ * @param spatializer the {@code Spatializer} instance whose head tracking mode is changing
+ * @param mode the new head tracking mode
+ */
+ void onHeadTrackingModeChanged(@NonNull Spatializer spatializer,
+ @HeadTrackingMode int mode);
+
+ /**
+ * Called when the desired head tracking mode of the spatializer changed
+ * @param spatializer the {@code Spatializer} instance whose head tracking mode was set
+ * @param mode the newly set head tracking mode
+ */
+ void onDesiredHeadTrackingModeChanged(@NonNull Spatializer spatializer,
+ @HeadTrackingModeSet int mode);
+ }
+
+ /**
+ * @hide
+ * An interface to be notified of updates to the head to soundstage pose, as represented by the
+ * current head tracking mode.
+ * @see #setOnHeadToSoundstagePoseUpdatedListener(Executor, OnHeadToSoundstagePoseUpdatedListener)
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ public interface OnHeadToSoundstagePoseUpdatedListener {
+ /**
+ * Called when the head to soundstage transform is updated
+ * @param spatializer the {@code Spatializer} instance affected by the pose update
+ * @param pose the new pose data representing the transform between the frame
+ * of reference for the current head tracking mode (see
+ * {@link #getHeadTrackingMode()}) and the device being tracked (for
+ * instance a pair of headphones with a head tracker).<br>
+ * The head pose data is represented as an array of six float values, where
+ * the first three values are the translation vector, and the next three
+ * are the rotation vector.
+ */
+ void onHeadToSoundstagePoseUpdated(@NonNull Spatializer spatializer,
+ @NonNull float[] pose);
+ }
+
+ /**
* Returns whether audio of the given {@link AudioFormat}, played with the given
* {@link AudioAttributes} can be spatialized.
* Note that the result reflects the capabilities of the device and may change when
@@ -320,6 +451,17 @@ public class Spatializer {
}
}
+ private final Object mStateListenerLock = new Object();
+ /**
+ * List of listeners for state listener and their associated Executor.
+ * List is lazy-initialized on first registration
+ */
+ @GuardedBy("mStateListenerLock")
+ private @Nullable ArrayList<StateListenerInfo> mStateListeners;
+
+ @GuardedBy("mStateListenerLock")
+ private @Nullable SpatializerInfoDispatcherStub mInfoDispatcherStub;
+
private final class SpatializerInfoDispatcherStub extends ISpatializerCallback.Stub {
@Override
public void dispatchSpatializerEnabledChanged(boolean enabled) {
@@ -401,4 +543,378 @@ public class Spatializer {
}
return false;
}
+
+
+ /**
+ * @hide
+ * Return the current head tracking mode as used by the system.
+ * Note this may differ from the desired head tracking mode. Reasons for the two to differ
+ * include: a head tracking device is not available for the current audio output device,
+ * the transmission conditions between the tracker and device have deteriorated and tracking
+ * has been disabled.
+ * @see #getDesiredHeadTrackingMode()
+ * @return the current head tracking mode
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public @HeadTrackingMode int getHeadTrackingMode() {
+ try {
+ return mAm.getService().getActualHeadTrackingMode();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getActualHeadTrackingMode", e);
+ return HEAD_TRACKING_MODE_UNSUPPORTED;
+ }
+
+ }
+
+ /**
+ * @hide
+ * Return the desired head tracking mode.
+ * Note this may differ from the actual head tracking mode, reflected by
+ * {@link #getHeadTrackingMode()}.
+ * @return the desired head tring mode
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public @HeadTrackingMode int getDesiredHeadTrackingMode() {
+ try {
+ return mAm.getService().getDesiredHeadTrackingMode();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getDesiredHeadTrackingMode", e);
+ return HEAD_TRACKING_MODE_UNSUPPORTED;
+ }
+ }
+
+ /**
+ * @hide
+ * Returns the list of supported head tracking modes.
+ * @return the list of modes that can be used in {@link #setDesiredHeadTrackingMode(int)} to
+ * enable head tracking. The list will be empty if {@link #getHeadTrackingMode()}
+ * is {@link #HEAD_TRACKING_MODE_UNSUPPORTED}. Values can be
+ * {@link #HEAD_TRACKING_MODE_OTHER},
+ * {@link #HEAD_TRACKING_MODE_RELATIVE_WORLD} or
+ * {@link #HEAD_TRACKING_MODE_RELATIVE_DEVICE}
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public @NonNull List<Integer> getSupportedHeadTrackingModes() {
+ try {
+ final int[] modes = mAm.getService().getSupportedHeadTrackingModes();
+ final ArrayList<Integer> list = new ArrayList<>(0);
+ for (int mode : modes) {
+ list.add(mode);
+ }
+ return list;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getSupportedHeadTrackModes", e);
+ return new ArrayList(0);
+ }
+ }
+
+ /**
+ * @hide
+ * Sets the desired head tracking mode.
+ * Note a set desired mode may differ from the actual head tracking mode.
+ * @see #getHeadTrackingMode()
+ * @param mode the desired head tracking mode, one of the values returned by
+ * {@link #getSupportedHeadTrackModes()}, or {@link #HEAD_TRACKING_MODE_DISABLED} to
+ * disable head tracking.
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public void setDesiredHeadTrackingMode(@HeadTrackingModeSet int mode) {
+ try {
+ mAm.getService().setDesiredHeadTrackingMode(mode);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling setDesiredHeadTrackingMode to " + mode, e);
+ }
+ }
+
+ /**
+ * @hide
+ * Recenters the head tracking at the current position / orientation.
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public void recenterHeadTracker() {
+ try {
+ mAm.getService().recenterHeadTracker();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling recenterHeadTracker", e);
+ }
+ }
+
+ /**
+ * @hide
+ * Adds a listener to be notified of changes to the head tracking mode of the
+ * {@code Spatializer}
+ * @param executor the {@code Executor} handling the callbacks
+ * @param listener the listener to register
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public void addOnHeadTrackingModeChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnHeadTrackingModeChangedListener listener) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
+ synchronized (mHeadTrackingListenerLock) {
+ if (hasListener(listener, mHeadTrackingListeners)) {
+ throw new IllegalArgumentException(
+ "Called addOnHeadTrackingModeChangedListener() "
+ + "on a previously registered listener");
+ }
+ // lazy initialization of the list of strategy-preferred device listener
+ if (mHeadTrackingListeners == null) {
+ mHeadTrackingListeners = new ArrayList<>();
+ }
+ mHeadTrackingListeners.add(
+ new ListenerInfo<OnHeadTrackingModeChangedListener>(listener, executor));
+ if (mHeadTrackingListeners.size() == 1) {
+ // register binder for callbacks
+ if (mHeadTrackingDispatcherStub == null) {
+ mHeadTrackingDispatcherStub =
+ new SpatializerHeadTrackingDispatcherStub();
+ }
+ try {
+ mAm.getService().registerSpatializerHeadTrackingCallback(
+ mHeadTrackingDispatcherStub);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Removes a previously added listener for changes to the head tracking mode of the
+ * {@code Spatializer}.
+ * @param listener the listener to unregister
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public void removeOnHeadTrackingModeChangedListener(
+ @NonNull OnHeadTrackingModeChangedListener listener) {
+ Objects.requireNonNull(listener);
+ synchronized (mHeadTrackingListenerLock) {
+ if (!removeListener(listener, mHeadTrackingListeners)) {
+ throw new IllegalArgumentException(
+ "Called removeOnHeadTrackingModeChangedListener() "
+ + "on an unregistered listener");
+ }
+ if (mHeadTrackingListeners.size() == 0) {
+ // unregister binder for callbacks
+ try {
+ mAm.getService().unregisterSpatializerHeadTrackingCallback(
+ mHeadTrackingDispatcherStub);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ mHeadTrackingDispatcherStub = null;
+ mHeadTrackingListeners = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Set the listener to receive head to soundstage pose updates.
+ * @param executor the {@code Executor} handling the callbacks
+ * @param listener the listener to register
+ * @see #clearOnHeadToSoundstagePoseUpdatedListener()
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public void setOnHeadToSoundstagePoseUpdatedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnHeadToSoundstagePoseUpdatedListener listener) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
+ synchronized (mPoseListenerLock) {
+ if (mPoseListener != null) {
+ throw new IllegalStateException("Trying to overwrite existing listener");
+ }
+ mPoseListener =
+ new ListenerInfo<OnHeadToSoundstagePoseUpdatedListener>(listener, executor);
+ mPoseDispatcher = new SpatializerPoseDispatcherStub();
+ try {
+ mAm.getService().registerHeadToSoundstagePoseCallback(mPoseDispatcher);
+ } catch (RemoteException e) {
+ mPoseListener = null;
+ mPoseDispatcher = null;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Clears the listener for head to soundstage pose updates
+ * @see #setOnHeadToSoundstagePoseUpdatedListener(Executor, OnHeadToSoundstagePoseUpdatedListener)
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public void clearOnHeadToSoundstagePoseUpdatedListener() {
+ synchronized (mPoseListenerLock) {
+ if (mPoseDispatcher == null) {
+ throw (new IllegalStateException("No listener to clear"));
+ }
+ try {
+ mAm.getService().unregisterHeadToSoundstagePoseCallback(mPoseDispatcher);
+ } catch (RemoteException e) { }
+ mPoseListener = null;
+ mPoseDispatcher = null;
+ }
+ }
+
+ /**
+ * @hide
+ * Sets an additional transform over the soundstage.
+ * The transform represents the pose of the soundstage, relative
+ * to either the device (in {@link #HEAD_TRACKING_MODE_RELATIVE_DEVICE} mode), the world (in
+ * {@link #HEAD_TRACKING_MODE_RELATIVE_WORLD}) or the listener’s head (in
+ * {@link #HEAD_TRACKING_MODE_DISABLED} mode).
+ * @param transform an array of 6 float values, the first 3 are the translation vector, the
+ * other 3 are the rotation vector.
+ */
+ @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+ @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+ public void setGlobalTransform(@NonNull float[] transform) {
+ if (Objects.requireNonNull(transform).length != 6) {
+ throw new IllegalArgumentException("transform array must be of size 6, was "
+ + transform.length);
+ }
+ try {
+ mAm.getService().setSpatializerGlobalTransform(transform);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling setGlobalTransform", e);
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ // callback helper definitions
+
+ private static class ListenerInfo<T> {
+ final @NonNull T mListener;
+ final @NonNull Executor mExecutor;
+
+ ListenerInfo(T listener, Executor exe) {
+ mListener = listener;
+ mExecutor = exe;
+ }
+ }
+
+ private static <T> ListenerInfo<T> getListenerInfo(
+ T listener, ArrayList<ListenerInfo<T>> listeners) {
+ if (listeners == null) {
+ return null;
+ }
+ for (ListenerInfo<T> info : listeners) {
+ if (info.mListener == listener) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ private static <T> boolean hasListener(T listener, ArrayList<ListenerInfo<T>> listeners) {
+ return getListenerInfo(listener, listeners) != null;
+ }
+
+ private static <T> boolean removeListener(T listener, ArrayList<ListenerInfo<T>> listeners) {
+ final ListenerInfo<T> infoToRemove = getListenerInfo(listener, listeners);
+ if (infoToRemove != null) {
+ listeners.remove(infoToRemove);
+ return true;
+ }
+ return false;
+ }
+
+ //-----------------------------------------------------------------------------
+ // head tracking callback management and stub
+
+ private final Object mHeadTrackingListenerLock = new Object();
+ /**
+ * List of listeners for head tracking mode listener and their associated Executor.
+ * List is lazy-initialized on first registration
+ */
+ @GuardedBy("mHeadTrackingListenerLock")
+ private @Nullable ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>>
+ mHeadTrackingListeners;
+
+ @GuardedBy("mHeadTrackingListenerLock")
+ private @Nullable SpatializerHeadTrackingDispatcherStub mHeadTrackingDispatcherStub;
+
+ private final class SpatializerHeadTrackingDispatcherStub
+ extends ISpatializerHeadTrackingModeCallback.Stub {
+ @Override
+ public void dispatchSpatializerActualHeadTrackingModeChanged(int mode) {
+ // make a shallow copy of listeners so callback is not executed under lock
+ final ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>> headTrackingListeners;
+ synchronized (mHeadTrackingListenerLock) {
+ if (mHeadTrackingListeners == null || mHeadTrackingListeners.size() == 0) {
+ return;
+ }
+ headTrackingListeners = (ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>>)
+ mHeadTrackingListeners.clone();
+ }
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ for (ListenerInfo<OnHeadTrackingModeChangedListener> info : headTrackingListeners) {
+ info.mExecutor.execute(() -> info.mListener
+ .onHeadTrackingModeChanged(Spatializer.this, mode));
+ }
+ }
+ }
+
+ @Override
+ public void dispatchSpatializerDesiredHeadTrackingModeChanged(int mode) {
+ // make a shallow copy of listeners so callback is not executed under lock
+ final ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>> headTrackingListeners;
+ synchronized (mHeadTrackingListenerLock) {
+ if (mHeadTrackingListeners == null || mHeadTrackingListeners.size() == 0) {
+ return;
+ }
+ headTrackingListeners = (ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>>)
+ mHeadTrackingListeners.clone();
+ }
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ for (ListenerInfo<OnHeadTrackingModeChangedListener> info : headTrackingListeners) {
+ info.mExecutor.execute(() -> info.mListener
+ .onDesiredHeadTrackingModeChanged(Spatializer.this, mode));
+ }
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ // head pose callback management and stub
+ private final Object mPoseListenerLock = new Object();
+ /**
+ * Listener for head to soundstage updates
+ */
+ @GuardedBy("mPoseListenerLock")
+ private @Nullable ListenerInfo<OnHeadToSoundstagePoseUpdatedListener> mPoseListener;
+ @GuardedBy("mPoseListenerLock")
+ private @Nullable SpatializerPoseDispatcherStub mPoseDispatcher;
+
+ private final class SpatializerPoseDispatcherStub
+ extends ISpatializerHeadToSoundStagePoseCallback.Stub {
+
+ @Override
+ public void dispatchPoseChanged(float[] pose) {
+ // make a copy of ref to listener so callback is not executed under lock
+ final ListenerInfo<OnHeadToSoundstagePoseUpdatedListener> listener;
+ synchronized (mPoseListenerLock) {
+ listener = mPoseListener;
+ }
+ if (listener == null) {
+ return;
+ }
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ listener.mExecutor.execute(() -> listener.mListener
+ .onHeadToSoundstagePoseUpdated(Spatializer.this, pose));
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml
index 50bfbe07b205..89739f6f6fc4 100644
--- a/packages/SettingsLib/res/values-as/arrays.xml
+++ b/packages/SettingsLib/res/values-as/arrays.xml
@@ -228,7 +228,7 @@
<item msgid="8612549335720461635">"৪কে. (সুৰক্ষিত)"</item>
<item msgid="7322156123728520872">"৪কে. (বৰ্ধিত)"</item>
<item msgid="7735692090314849188">"৪কে. (বৰ্ধিত, সুৰক্ষিত)"</item>
- <item msgid="7346816300608639624">"৭২০পি., ১০৮০পি. (দ্বৈত স্ক্ৰীণ)"</item>
+ <item msgid="7346816300608639624">"৭২০পি., ১০৮০পি. (দ্বৈত স্ক্ৰীন)"</item>
</string-array>
<string-array name="enable_opengl_traces_entries">
<item msgid="4433736508877934305">"নাই"</item>
@@ -243,7 +243,7 @@
</string-array>
<string-array name="track_frame_time_entries">
<item msgid="634406443901014984">"অফ হৈ আছে"</item>
- <item msgid="1288760936356000927">"স্ক্ৰীণত দণ্ড হিচাপে"</item>
+ <item msgid="1288760936356000927">"স্ক্ৰীনত দণ্ড হিচাপে"</item>
<item msgid="5023908510820531131">"<xliff:g id="AS_TYPED_COMMAND">adb shell dumpsys gfxinfo</xliff:g>ত"</item>
</string-array>
<string-array name="debug_hw_overdraw_entries">
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 1ae3452ac847..9f9b11d1e3b1 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -238,7 +238,7 @@
<string name="bugreport_in_power" msgid="8664089072534638709">"বাগ ৰিপৰ্টৰ শ্ৱৰ্টকাট"</string>
<string name="bugreport_in_power_summary" msgid="1885529649381831775">"পাৱাৰ মেনুত বাগ প্ৰতিবেদন গ্ৰহণ কৰিবলৈ এটা বুটাম দেখুৱাওক"</string>
<string name="keep_screen_on" msgid="1187161672348797558">"জাগ্ৰত কৰি ৰাখক"</string>
- <string name="keep_screen_on_summary" msgid="1510731514101925829">"চ্চাৰ্জ হৈ থকাৰ সময়ত স্ক্ৰীণ কেতিয়াও সুপ্ত অৱস্থালৈ নাযায়"</string>
+ <string name="keep_screen_on_summary" msgid="1510731514101925829">"চ্চাৰ্জ হৈ থকাৰ সময়ত স্ক্ৰীন কেতিয়াও সুপ্ত অৱস্থালৈ নাযায়"</string>
<string name="bt_hci_snoop_log" msgid="7291287955649081448">"ব্লুটুথ HCI স্নুপ ল’গ সক্ষম কৰক"</string>
<string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"ব্লুটুথ পেকেট সংগ্ৰহ কৰক। (এই ছেটিংটো সলনি কৰাৰ পিছত ব্লুটুথ ট’গল কৰক)"</string>
<string name="oem_unlock_enable" msgid="5334869171871566731">"ঔইএম আনলক"</string>
@@ -331,9 +331,9 @@
<string name="media_category" msgid="8122076702526144053">"মিডিয়া"</string>
<string name="debug_monitoring_category" msgid="1597387133765424994">"নিৰীক্ষণ কৰি থকা হৈছে"</string>
<string name="strict_mode" msgid="889864762140862437">"কঠোৰ ম’ড সক্ষম কৰা হৈছে"</string>
- <string name="strict_mode_summary" msgid="1838248687233554654">"যেতিয়া এপসমূহে মুখ্য থ্ৰেডত দীঘলীয়া কাৰ্যকলাপ চলাই, তেতিয়া স্ক্ৰীণ ফ্লাশ্ব কৰক"</string>
+ <string name="strict_mode_summary" msgid="1838248687233554654">"যেতিয়া এপ্সমূহে মুখ্য থ্ৰেডত দীঘলীয়া কাৰ্যকলাপ চলাই, তেতিয়া স্ক্ৰীন ফ্লাশ্ব কৰক"</string>
<string name="pointer_location" msgid="7516929526199520173">"পইণ্টাৰৰ অৱস্থান"</string>
- <string name="pointer_location_summary" msgid="957120116989798464">"চলিত স্পৰ্শ-বিষয়ক তথ্যসহ স্ক্ৰীণ অভাৰলে\'"</string>
+ <string name="pointer_location_summary" msgid="957120116989798464">"চলিত স্পৰ্শ-বিষয়ক তথ্যসহ স্ক্ৰীন অভাৰলে’"</string>
<string name="show_touches" msgid="8437666942161289025">"টেপসমূহ দেখুৱাওক"</string>
<string name="show_touches_summary" msgid="3692861665994502193">"টিপিলে দৃশ্যায়িত ফীডবেক দিয়ক"</string>
<string name="show_screen_updates" msgid="2078782895825535494">"পৃষ্ঠভাগৰ আপডেইট দেখুৱাওক"</string>
@@ -344,7 +344,7 @@
<string name="show_hw_layers_updates_summary" msgid="5850955890493054618">"হাৰ্ডৱেৰ লেয়াৰ আপডেইট হওতে সিঁহতক সেউজীয়া ৰঙেৰে ফ্লাশ্ব কৰক"</string>
<string name="debug_hw_overdraw" msgid="8944851091008756796">"GPU অভাৰড্ৰ ডিবাগ কৰক"</string>
<string name="disable_overlays" msgid="4206590799671557143">"HW অ’ভাৰলে অক্ষম কৰক"</string>
- <string name="disable_overlays_summary" msgid="1954852414363338166">"স্ক্ৰীণ কম্প’জিট কৰাৰ বাবে সদায় জিপিইউ ব্যৱহাৰ কৰক"</string>
+ <string name="disable_overlays_summary" msgid="1954852414363338166">"স্ক্ৰীন কম্প’জিট কৰাৰ বাবে সদায় জিপিইউ ব্যৱহাৰ কৰক"</string>
<string name="simulate_color_space" msgid="1206503300335835151">"ৰঙৰ ঠাই ছিমিউলেইট কৰক"</string>
<string name="enable_opengl_traces_title" msgid="4638773318659125196">"OpenGL ট্ৰেছ সক্ষম কৰক"</string>
<string name="usb_audio_disable_routing" msgid="3367656923544254975">"ইউএছবি অডিঅ\' ৰাউটিং অক্ষম কৰক"</string>
@@ -352,7 +352,7 @@
<string name="debug_layout" msgid="1659216803043339741">"লেআউটৰ সময় দেখুৱাওক"</string>
<string name="debug_layout_summary" msgid="8825829038287321978">"ক্লিপ বাউণ্ড, মাৰ্জিন আদিসমূহ দেখুৱাওক"</string>
<string name="force_rtl_layout_all_locales" msgid="8690762598501599796">"আৰটিএল চানেকিৰ দিশ বলেৰে সলনি কৰক"</string>
- <string name="force_rtl_layout_all_locales_summary" msgid="6663016859517239880">"সকলো ভাষাৰ বাবে স্ক্ৰীণৰ চানেকিৰ দিশ RTLলৈ বলেৰে সলনি কৰক"</string>
+ <string name="force_rtl_layout_all_locales_summary" msgid="6663016859517239880">"সকলো ভাষাৰ বাবে স্ক্ৰীনৰ চানেকিৰ দিশ RTLলৈ বলেৰে সলনি কৰক"</string>
<string name="window_blurs" msgid="6831008984828425106">"ৱিণ্ড’ স্তৰত অস্পষ্ট কৰাৰ অনুমতি দিয়ক"</string>
<string name="force_msaa" msgid="4081288296137775550">"বল ৪গুণ MSAA"</string>
<string name="force_msaa_summary" msgid="9070437493586769500">"OpenGL ES 2.0 এপত ৪গুণ MSAA সক্ষম কৰক"</string>
@@ -373,7 +373,7 @@
<string name="show_all_anrs" msgid="9160563836616468726">"নেপথ্য এএনআৰবোৰ দেখুৱাওক"</string>
<string name="show_all_anrs_summary" msgid="8562788834431971392">"নেপথ্য এপসমূহৰ বাবে এপে সঁহাৰি দিয়া নাই ডায়ল\'গ প্ৰদৰ্শন কৰক"</string>
<string name="show_notification_channel_warnings" msgid="3448282400127597331">"জাননী চ্চেনেলৰ সকীয়নিসমূহ দেখুৱাওক"</string>
- <string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"কোনো এপে বৈধ চ্চেনেল নোহোৱাকৈ কোনো জাননী প\'ষ্ট কৰিলে স্ক্ৰীণত সকীয়নি প্ৰদৰ্শন হয়"</string>
+ <string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"কোনো এপে বৈধ চ্চেনেল নোহোৱাকৈ কোনো জাননী প\'ষ্ট কৰিলে স্ক্ৰীনত সকীয়নি প্ৰদৰ্শন হয়"</string>
<string name="force_allow_on_external" msgid="9187902444231637880">"বাহ্যিক সঞ্চয়াগাৰত এপক বলেৰে অনুমতি দিয়ক"</string>
<string name="force_allow_on_external_summary" msgid="8525425782530728238">"মেনিফেষ্টৰ মান যিয়েই নহওক, বাহ্যিক সঞ্চয়াগাৰত লিখিবলৈ যিকোনো এপক উপযুক্ত কৰি তোলে"</string>
<string name="force_resizable_activities" msgid="7143612144399959606">"বলেৰে কাৰ্যকলাপসমূহৰ আকাৰ সলনি কৰিব পৰা কৰক"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 921caba34e13..108d86d185e0 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -286,7 +286,7 @@
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Réduit la décharge de la batterie et améliore les performances du réseau"</string>
<string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Quand ce mode est activé, l\'adresse MAC de cet appareil peut changer chaque fois qu\'il se connecte à un réseau Wi-Fi où le changement aléatoire d\'adresse MAC est activé"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Facturé à l\'usage"</string>
- <string name="wifi_unmetered_label" msgid="6174142840934095093">"Non facturé à l\'usage"</string>
+ <string name="wifi_unmetered_label" msgid="6174142840934095093">"Sans compteur"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Tailles des tampons de l\'enregistreur"</string>
<string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Tailles enreg. par tampon journal"</string>
<string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"Effacer l\'espace de stockage persistant de l\'enregistreur ?"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index cbf92af607d2..0f381c966932 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -542,7 +542,7 @@
<string name="delete_blob_text" msgid="2819192607255625697">"Ортақ деректерді жою"</string>
<string name="delete_blob_confirmation_text" msgid="7807446938920827280">"Осы ортақ деректерді шынымен жойғыңыз келе ме?"</string>
<string name="user_add_user_item_summary" msgid="5748424612724703400">"Пайдаланушылардың өздерінің қолданбалары мен мазмұны болады"</string>
- <string name="user_add_profile_item_summary" msgid="5418602404308968028">"Өз есептік жазбаңыздан қолданбалар мен мазмұнға қол жетімділікті шектеуіңізге болады"</string>
+ <string name="user_add_profile_item_summary" msgid="5418602404308968028">"Өз аккаунтыңыздан қолданбалар мен мазмұнға қол жетімділікті шектеуіңізге болады"</string>
<string name="user_add_user_item_title" msgid="2394272381086965029">"Пайдаланушы"</string>
<string name="user_add_profile_item_title" msgid="3111051717414643029">"Шектелген профайл"</string>
<string name="user_add_user_title" msgid="5457079143694924885">"Жаңа пайдаланушы қосылсын ба?"</string>
diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml
index 801c037684f9..195fc47e8c64 100644
--- a/packages/SettingsLib/res/values-ko/arrays.xml
+++ b/packages/SettingsLib/res/values-ko/arrays.xml
@@ -28,7 +28,7 @@
<item msgid="2837871868181677206">"IP 주소를 가져오는 중..."</item>
<item msgid="4613015005934755724">"연결됨"</item>
<item msgid="3763530049995655072">"일시 정지됨"</item>
- <item msgid="7852381437933824454">"연결을 끊는 중…"</item>
+ <item msgid="7852381437933824454">"연결 해제 중…"</item>
<item msgid="5046795712175415059">"연결 끊김"</item>
<item msgid="2473654476624070462">"실패"</item>
<item msgid="9146847076036105115">"차단됨"</item>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 97ad0a0ef347..c455c14b1226 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -65,7 +65,7 @@
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"만료됨"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"연결 끊김"</string>
- <string name="bluetooth_disconnecting" msgid="7638892134401574338">"연결을 끊는 중…"</string>
+ <string name="bluetooth_disconnecting" msgid="7638892134401574338">"연결 해제 중…"</string>
<string name="bluetooth_connecting" msgid="5871702668260192755">"연결 중…"</string>
<string name="bluetooth_connected" msgid="8065345572198502293">"연결됨<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_pairing" msgid="4269046942588193600">"페어링 중..."</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
index c501b3aab4d4..2e8f36834584 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
@@ -110,6 +110,18 @@ public class PowerAllowlistBackend {
}
/**
+ * Check if target package is in allow list except idle app
+ */
+ public boolean isAllowlistedExceptIdle(String pkg) {
+ try {
+ return mDeviceIdleService.isPowerSaveWhitelistExceptIdleApp(pkg);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to reach IDeviceIdleController", e);
+ return true;
+ }
+ }
+
+ /**
*
* @param pkgs a list of packageName
* @return true when one of package is in allow list
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
index 4f11fb1f782f..6caf7624e1bc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
@@ -151,4 +151,14 @@ public class PowerAllowlistBackendTest {
assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_TWO)).isFalse();
assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse();
}
+
+ @Test
+ public void testIsPowerSaveWhitelistExceptIdleApp() throws Exception {
+ doReturn(true).when(mDeviceIdleService)
+ .isPowerSaveWhitelistExceptIdleApp(PACKAGE_ONE);
+
+ mPowerAllowlistBackend.refreshList();
+
+ assertThat(mPowerAllowlistBackend.isAllowlistedExceptIdle(PACKAGE_ONE)).isTrue();
+ }
}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index d051290cb4b4..6671308ef66a 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -92,6 +92,7 @@ android_library {
"iconloader_base",
"SystemUI-tags",
"SystemUI-proto",
+ "monet",
"dagger2",
"jsr330",
"lottie",
@@ -179,6 +180,7 @@ android_library {
"mockito-target-extended-minus-junit4",
"testables",
"truth-prebuilt",
+ "monet",
"dagger2",
"jsr330",
"WindowManager-Shell",
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 0d18b8dea284..e509777633e7 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -22,6 +22,9 @@
},
{
"exclude-annotation": "android.platform.test.annotations.Postsubmit"
+ },
+ {
+ "exclude-annotation": "android.platform.test.scenario.annotation.LargeScreenOnly"
}
]
},
@@ -82,6 +85,9 @@
},
{
"exclude-annotation": "android.platform.helpers.Staging"
+ },
+ {
+ "exclude-annotation": "android.platform.test.scenario.annotation.LargeScreenOnly"
}
]
}
@@ -101,6 +107,9 @@
},
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.scenario.annotation.LargeScreenOnly"
}
]
}
@@ -117,5 +126,24 @@
}
]
}
+ ],
+ "large-screen-postsubmit": [
+ {
+ "name": "PlatformScenarioTests",
+ "options" : [
+ {
+ "include-filter": "android.platform.test.scenario.sysui"
+ },
+ {
+ "include-annotation": "android.platform.test.scenario.annotation.LargeScreenOnly"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
]
}
diff --git a/packages/SystemUI/monet/Android.bp b/packages/SystemUI/monet/Android.bp
new file mode 100644
index 000000000000..507ea25083e1
--- /dev/null
+++ b/packages/SystemUI/monet/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+ name: "monet",
+ platform_apis: true,
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.core_core",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+}
diff --git a/packages/SystemUI/monet/AndroidManifest.xml b/packages/SystemUI/monet/AndroidManifest.xml
new file mode 100644
index 000000000000..1fab52877847
--- /dev/null
+++ b/packages/SystemUI/monet/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.systemui.monet">
+</manifest>
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
new file mode 100644
index 000000000000..b8039e13b2a5
--- /dev/null
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.monet
+
+import android.annotation.ColorInt
+import android.app.WallpaperColors
+import android.graphics.Color
+import com.android.internal.graphics.ColorUtils
+import com.android.internal.graphics.cam.Cam
+import com.android.internal.graphics.cam.CamUtils.lstarFromInt
+import kotlin.math.absoluteValue
+import kotlin.math.roundToInt
+
+const val TAG = "ColorScheme"
+
+const val ACCENT1_CHROMA = 48.0f
+const val ACCENT2_CHROMA = 16.0f
+const val ACCENT3_CHROMA = 32.0f
+const val ACCENT3_HUE_SHIFT = 60.0f
+
+const val NEUTRAL1_CHROMA = 4.0f
+const val NEUTRAL2_CHROMA = 8.0f
+
+const val GOOGLE_BLUE = 0xFF1b6ef3.toInt()
+
+const val MIN_CHROMA = 15
+const val MIN_LSTAR = 10
+
+public class ColorScheme(@ColorInt seed: Int, val darkTheme: Boolean) {
+
+ val accent1: List<Int>
+ val accent2: List<Int>
+ val accent3: List<Int>
+ val neutral1: List<Int>
+ val neutral2: List<Int>
+
+ constructor(wallpaperColors: WallpaperColors, darkTheme: Boolean):
+ this(getSeedColor(wallpaperColors), darkTheme)
+
+ val allAccentColors: List<Int>
+ get() {
+ val allColors = mutableListOf<Int>()
+ allColors.addAll(accent1)
+ allColors.addAll(accent2)
+ allColors.addAll(accent3)
+ return allColors
+ }
+
+ val allNeutralColors: List<Int>
+ get() {
+ val allColors = mutableListOf<Int>()
+ allColors.addAll(neutral1)
+ allColors.addAll(neutral2)
+ return allColors
+ }
+
+ val backgroundColor
+ get() = ColorUtils.setAlphaComponent(if (darkTheme) neutral1[8] else neutral1[0], 0xFF)
+
+ val accentColor
+ get() = ColorUtils.setAlphaComponent(if (darkTheme) accent1[2] else accent1[6], 0xFF)
+
+ init {
+ val seedArgb = if (seed == Color.TRANSPARENT) GOOGLE_BLUE else seed
+ val camSeed = Cam.fromInt(seedArgb)
+ val hue = camSeed.hue
+ val chroma = camSeed.chroma.coerceAtLeast(ACCENT1_CHROMA)
+ accent1 = Shades.of(hue, chroma).toList()
+ accent2 = Shades.of(hue, ACCENT2_CHROMA).toList()
+ accent3 = Shades.of(hue + ACCENT3_HUE_SHIFT, ACCENT3_CHROMA).toList()
+ neutral1 = Shades.of(hue, NEUTRAL1_CHROMA).toList()
+ neutral2 = Shades.of(hue, NEUTRAL2_CHROMA).toList()
+ }
+
+ override fun toString(): String {
+ return "ColorScheme {\n" +
+ " neutral1: ${humanReadable(neutral1)}\n" +
+ " neutral2: ${humanReadable(neutral2)}\n" +
+ " accent1: ${humanReadable(accent1)}\n" +
+ " accent2: ${humanReadable(accent2)}\n" +
+ " accent3: ${humanReadable(accent3)}\n" +
+ "}"
+ }
+
+ companion object {
+ /**
+ * Identifies a color to create a color scheme from.
+ *
+ * @param wallpaperColors Colors extracted from an image via quantization.
+ * @return ARGB int representing the color
+ */
+ @JvmStatic
+ @ColorInt
+ fun getSeedColor(wallpaperColors: WallpaperColors): Int {
+ return getSeedColors(wallpaperColors).first()
+ }
+
+ /**
+ * Filters and ranks colors from WallpaperColors.
+ *
+ * @param wallpaperColors Colors extracted from an image via quantization.
+ * @return List of ARGB ints, ordered from highest scoring to lowest.
+ */
+ @JvmStatic
+ fun getSeedColors(wallpaperColors: WallpaperColors): List<Int> {
+ val totalPopulation = wallpaperColors.allColors.values.reduce { a, b -> a + b }
+ .toDouble()
+ val totalPopulationMeaningless = (totalPopulation == 0.0)
+ if (totalPopulationMeaningless) {
+ // WallpaperColors with a population of 0 indicate the colors didn't come from
+ // quantization. Instead of scoring, trust the ordering of the provided primary
+ // secondary/tertiary colors.
+ //
+ // In this case, the colors are usually from a Live Wallpaper.
+ val distinctColors = wallpaperColors.mainColors.map {
+ it.toArgb()
+ }.distinct().filter {
+ val cam = Cam.fromInt(it)
+ val lstar = lstarFromInt(it)
+ cam.chroma >= MIN_CHROMA && lstar >= MIN_LSTAR
+ }.toList()
+
+ if (distinctColors.isEmpty()) {
+ return listOf(GOOGLE_BLUE)
+ }
+ return distinctColors
+ }
+
+ val intToProportion = wallpaperColors.allColors.mapValues {
+ it.value.toDouble() / totalPopulation
+ }
+ val intToCam = wallpaperColors.allColors.mapValues { Cam.fromInt(it.key) }
+
+ // Get an array with 360 slots. A slot contains the percentage of colors with that hue.
+ val hueProportions = huePopulations(intToCam, intToProportion)
+ // Map each color to the percentage of the image with its hue.
+ val intToHueProportion = wallpaperColors.allColors.mapValues {
+ val cam = intToCam[it.key]!!
+ val hue = cam.hue.roundToInt()
+ var proportion = 0.0
+ for (i in hue - 15..hue + 15) {
+ proportion += hueProportions[wrapDegrees(i)]
+ }
+ proportion
+ }
+ // Remove any inappropriate seed colors. For example, low chroma colors look grayscale
+ // raising their chroma will turn them to a much louder color that may not have been
+ // in the image.
+ val filteredIntToCam = intToCam.filter {
+ val cam = it.value
+ val lstar = lstarFromInt(it.key)
+ val proportion = intToHueProportion[it.key]!!
+ cam.chroma >= MIN_CHROMA && lstar >= MIN_LSTAR &&
+ (totalPopulationMeaningless || proportion > 0.01)
+ }
+ // Sort the colors by score, from high to low.
+ val seeds = mutableListOf<Int>()
+ val intToScoreIntermediate = filteredIntToCam.mapValues {
+ score(it.value, intToHueProportion[it.key]!!)
+ }
+ val intToScore = intToScoreIntermediate.entries.toMutableList()
+ intToScore.sortByDescending { it.value }
+
+ // Go through the colors, from high score to low score. If there isn't already a seed
+ // color with a hue close to color being examined, add the color being examined to the
+ // seed colors.
+ for (entry in intToScore) {
+ val int = entry.key
+ val existingSeedNearby = seeds.find {
+ val hueA = intToCam[int]!!.hue
+ val hueB = intToCam[it]!!.hue
+ hueDiff(hueA, hueB) < 15 } != null
+ if (existingSeedNearby) {
+ continue
+ }
+ seeds.add(int)
+ }
+
+ if (seeds.isEmpty()) {
+ // Use gBlue 500 if there are 0 colors
+ seeds.add(GOOGLE_BLUE)
+ }
+
+ return seeds
+ }
+
+ private fun wrapDegrees(degrees: Int): Int {
+ return when {
+ degrees < 0 -> {
+ (degrees % 360) + 360
+ }
+ degrees >= 360 -> {
+ degrees % 360
+ }
+ else -> {
+ degrees
+ }
+ }
+ }
+
+ private fun hueDiff(a: Float, b: Float): Float {
+ return 180f - ((a - b).absoluteValue - 180f).absoluteValue
+ }
+
+ private fun humanReadable(colors: List<Int>): String {
+ return colors.joinToString { "#" + Integer.toHexString(it) }
+ }
+
+ private fun score(cam: Cam, proportion: Double): Double {
+ val proportionScore = 0.7 * 100.0 * proportion
+ val chromaScore = if (cam.chroma < ACCENT1_CHROMA) 0.1 * (cam.chroma - ACCENT1_CHROMA)
+ else 0.3 * (cam.chroma - ACCENT1_CHROMA)
+ return chromaScore + proportionScore
+ }
+
+ private fun huePopulations(
+ camByColor: Map<Int, Cam>,
+ populationByColor: Map<Int, Double>
+ ): List<Double> {
+ val huePopulation = List(size = 360, init = { 0.0 }).toMutableList()
+
+ for (entry in populationByColor.entries) {
+ val population = populationByColor[entry.key]!!
+ val cam = camByColor[entry.key]!!
+ val hue = cam.hue.roundToInt() % 360
+ huePopulation[hue] = huePopulation[hue] + population
+ }
+
+ return huePopulation
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java b/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java
new file mode 100644
index 000000000000..498b7dd8658c
--- /dev/null
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/Shades.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.monet;
+
+
+import androidx.annotation.ColorInt;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
+
+
+/**
+ * Generate sets of colors that are shades of the same color
+ */
+@VisibleForTesting
+public class Shades {
+ /**
+ * Combining the ability to convert between relative luminance and perceptual luminance with
+ * contrast leads to a design system that can be based on a linear value to determine contrast,
+ * rather than a ratio.
+ *
+ * This codebase implements a design system that has that property, and as a result, we can
+ * guarantee that any shades 5 steps from each other have a contrast ratio of at least 4.5.
+ * 4.5 is the requirement for smaller text contrast in WCAG 2.1 and earlier.
+ *
+ * However, lstar 50 does _not_ have a contrast ratio >= 4.5 with lstar 100.
+ * lstar 49.6 is the smallest lstar that will lead to a contrast ratio >= 4.5 with lstar 100,
+ * and it also contrasts >= 4.5 with lstar 100.
+ */
+ public static final float MIDDLE_LSTAR = 49.6f;
+
+ /**
+ * Generate shades of a color. Ordered in lightness _descending_.
+ * <p>
+ * The first shade will be at 95% lightness, the next at 90, 80, etc. through 0.
+ *
+ * @param hue hue in CAM16 color space
+ * @param chroma chroma in CAM16 color space
+ * @return shades of a color, as argb integers. Ordered by lightness descending.
+ */
+ public static @ColorInt int[] of(float hue, float chroma) {
+ int[] shades = new int[12];
+ shades[0] = ColorUtils.CAMToColor(hue, chroma, 99);
+ shades[1] = ColorUtils.CAMToColor(hue, chroma, 95);
+ for (int i = 2; i < 12; i++) {
+ float lStar = (i == 6) ? MIDDLE_LSTAR : 100 - 10 * (i - 1);
+ shades[i] = ColorUtils.CAMToColor(hue, chroma, lStar);
+ }
+ return shades;
+ }
+}
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 871b1c4eb3f6..624ee9f51b2a 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -146,4 +146,8 @@
<item name="android:shadowColor">@color/keyguard_shadow_color</item>
<item name="android:shadowRadius">?attr/shadowRadius</item>
</style>
+
+ <style name="TextAppearance.Keyguard.BottomArea.Button">
+ <item name="android:shadowRadius">0</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/drawable/logout_button_background.xml b/packages/SystemUI/res/drawable/logout_button_background.xml
index eafd663f19d9..34434be7aefb 100644
--- a/packages/SystemUI/res/drawable/logout_button_background.xml
+++ b/packages/SystemUI/res/drawable/logout_button_background.xml
@@ -17,7 +17,8 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <solid android:color="@color/logout_button_bg_color"/>
+ <solid android:color="?androidprv:attr/colorAccentPrimary"/>
<corners android:radius="@dimen/logout_button_corner_radius"/>
</shape>
diff --git a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml
index a21a63c3e7c3..9cf09ff328c4 100644
--- a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml
+++ b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml
@@ -15,19 +15,25 @@
~ limitations under the License
-->
<!-- This is a view that shows a user switcher in Keyguard. -->
-<com.android.systemui.statusbar.phone.UserAvatarView
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/keyguard_qs_user_switch_view"
- android:layout_width="@dimen/kg_framed_avatar_size"
- android:layout_height="@dimen/kg_framed_avatar_size"
- android:layout_centerHorizontal="true"
- android:layout_gravity="top|end"
- android:layout_marginEnd="16dp"
- systemui:avatarPadding="0dp"
- systemui:badgeDiameter="18dp"
- systemui:badgeMargin="1dp"
- systemui:frameColor="@color/kg_user_avatar_frame"
- systemui:framePadding="0dp"
- systemui:frameWidth="0dp">
-</com.android.systemui.statusbar.phone.UserAvatarView>
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="end">
+ <com.android.systemui.statusbar.phone.UserAvatarView
+ android:id="@+id/kg_multi_user_avatar"
+ android:layout_width="@dimen/kg_framed_avatar_size"
+ android:layout_height="@dimen/kg_framed_avatar_size"
+ android:layout_centerHorizontal="true"
+ android:layout_gravity="top|end"
+ android:layout_marginEnd="16dp"
+ systemui:avatarPadding="0dp"
+ systemui:badgeDiameter="18dp"
+ systemui:badgeMargin="1dp"
+ systemui:frameColor="@color/kg_user_avatar_frame"
+ systemui:framePadding="0dp"
+ systemui:frameWidth="0dp">
+ </com.android.systemui.statusbar.phone.UserAvatarView>
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/sidefps_view.xml b/packages/SystemUI/res/layout/sidefps_view.xml
index 397763531eb9..921f78830981 100644
--- a/packages/SystemUI/res/layout/sidefps_view.xml
+++ b/packages/SystemUI/res/layout/sidefps_view.xml
@@ -14,11 +14,13 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.biometrics.SidefpsView
+<com.airbnb.lottie.LottieAnimationView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:id="@+id/sidefps_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:contentDescription="@string/accessibility_fingerprint_label">
-</com.android.systemui.biometrics.SidefpsView>
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/sidefps_animation"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:lottie_autoPlay="true"
+ app:lottie_loop="true"
+ app:lottie_rawRes="@raw/sfps_pulse"
+ android:contentDescription="@string/accessibility_fingerprint_label"/>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 435575104f0b..4b6e58fbd1e5 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -70,6 +70,19 @@
android:padding="@dimen/lock_icon_padding"
android:layout_gravity="center"
android:scaleType="centerCrop"/>
+
+ <!-- Fingerprint -->
+ <!-- AOD dashed fingerprint icon with moving dashes -->
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/lock_udfps_aod_fp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="@dimen/lock_icon_padding"
+ android:layout_gravity="center"
+ android:scaleType="centerCrop"
+ systemui:lottie_autoPlay="false"
+ systemui:lottie_loop="true"
+ systemui:lottie_rawRes="@raw/udfps_aod_fp"/>
</com.android.keyguard.LockIconView>
<com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
diff --git a/packages/SystemUI/res/layout/text_toast.xml b/packages/SystemUI/res/layout/text_toast.xml
index 49b182addd05..a3fe8efa48bf 100644
--- a/packages/SystemUI/res/layout/text_toast.xml
+++ b/packages/SystemUI/res/layout/text_toast.xml
@@ -45,6 +45,5 @@
android:maxLines="2"
android:paddingTop="12dp"
android:paddingBottom="12dp"
- android:lineHeight="20sp"
android:textAppearance="@*android:style/TextAppearance.Toast"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index 687830d5c7b3..0fcbfa161ddf 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -20,7 +20,7 @@
android:id="@+id/udfps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- systemui:sensorTouchAreaCoefficient="0.75"
+ systemui:sensorTouchAreaCoefficient="1.0"
android:contentDescription="@string/accessibility_fingerprint_label">
<ViewStub
diff --git a/packages/SystemUI/res/raw/sfps_pulse.json b/packages/SystemUI/res/raw/sfps_pulse.json
new file mode 100644
index 000000000000..c4903a2857a1
--- /dev/null
+++ b/packages/SystemUI/res/raw/sfps_pulse.json
@@ -0,0 +1 @@
+{"v":"5.7.6","fr":60,"ip":0,"op":300,"w":42,"h":80,"nm":"Fingerprint Pulse Motion","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[28,40,0],"to":[0.751,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[32.503,40,0],"to":[0,0,0],"ti":[0.751,0,0]},{"t":300,"s":[28,40,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.218,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[41.878,40,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.253,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[20]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[20]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[20]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[20]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[20]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[20]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[20]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[20]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[20]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[20]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[20]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":270,"op":330,"st":270,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/sfps_pulse_landscape.json b/packages/SystemUI/res/raw/sfps_pulse_landscape.json
new file mode 100644
index 000000000000..8c91762d7286
--- /dev/null
+++ b/packages/SystemUI/res/raw/sfps_pulse_landscape.json
@@ -0,0 +1 @@
+{"v":"5.7.6","fr":60,"ip":0,"op":300,"w":80,"h":42,"nm":"Fingerprint Pulse Motion Portrait","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":0,"s":[40,14,0],"to":[0,-0.751,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[40,14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[40,9.497,0],"to":[0,0,0],"ti":[0,-0.751,0]},{"t":300,"s":[40,14,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.218,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,0.122,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.253,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[20]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[20]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[20]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[20]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[20]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[20]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[20]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[20]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[20]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[20]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[20]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[40,16.413,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.40000000596,0.615686297417,0.964705884457,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":270,"op":330,"st":270,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml
index 19ec8cee18a8..f057603e2cd0 100644
--- a/packages/SystemUI/res/values-h800dp/dimens.xml
+++ b/packages/SystemUI/res/values-h800dp/dimens.xml
@@ -16,7 +16,7 @@
<resources>
<!-- Minimum margin between clock and top of screen or ambient indication -->
- <dimen name="keyguard_clock_top_margin">76dp</dimen>
+ <dimen name="keyguard_clock_top_margin">38dp</dimen>
<!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) -->
<dimen name="large_clock_text_size">200dp</dimen>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 2c19212581b7..08778bf14c66 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -192,9 +192,6 @@
<color name="udfps_enroll_progress">#ff669DF6</color> <!-- blue 400 -->
<color name="udfps_enroll_progress_help">#ffEE675C</color> <!-- red 400 -->
- <!-- Logout button -->
- <color name="logout_button_bg_color">#ccffffff</color>
-
<!-- Color for the Assistant invocation lights -->
<color name="default_invocation_lights_color">#ffffffff</color> <!-- white -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 37cc42ee4b73..5e495f0cd1a6 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -197,11 +197,6 @@
low powered state yet. -->
<bool name="doze_long_press_uses_prox">true</bool>
- <!-- Doze: whether the brightness sensor uses the proximity sensor.
- If both this parameter and doze_selectively_register_prox are true, registration for the
- brightness sensor won't occur when the display state is ON. -->
- <bool name="doze_brightness_uses_prox">true</bool>
-
<!-- Doze: should notifications be used as a pulse signal? -->
<bool name="doze_pulse_on_notifications">true</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 26ee5ea7f914..79581e183c4d 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -750,9 +750,7 @@
<!-- The margin between the status view and the notifications on Keyguard.-->
<dimen name="keyguard_status_view_bottom_margin">20dp</dimen>
<!-- Minimum margin between clock and status bar -->
- <dimen name="keyguard_clock_top_margin">36dp</dimen>
- <!-- The margin between top of clock and bottom of lock icon. -->
- <dimen name="keyguard_clock_lock_margin">16dp</dimen>
+ <dimen name="keyguard_clock_top_margin">18dp</dimen>
<!-- The amount to shift the clocks during a small/large transition -->
<dimen name="keyguard_clock_switch_y_shift">10dp</dimen>
<!-- When large clock is showing, offset the smartspace by this amount -->
@@ -1157,9 +1155,9 @@
<dimen name="default_burn_in_prevention_offset">15dp</dimen>
<!-- The maximum offset for the under-display fingerprint sensor (UDFPS) icon in either
- direction that elements aer moved to prevent burn-in on AOD-->
- <dimen name="udfps_burn_in_offset_x">2dp</dimen>
- <dimen name="udfps_burn_in_offset_y">8dp</dimen>
+ direction that elements are moved to prevent burn-in on AOD-->
+ <dimen name="udfps_burn_in_offset_x">7px</dimen>
+ <dimen name="udfps_burn_in_offset_y">28px</dimen>
<dimen name="corner_size">8dp</dimen>
<dimen name="top_padding">0dp</dimen>
@@ -1250,10 +1248,7 @@
<integer name="wired_charging_keyguard_text_animation_distance">-30</integer>
<!-- Logout button -->
- <dimen name="logout_button_layout_height">32dp</dimen>
- <dimen name="logout_button_padding_horizontal">16dp</dimen>
- <dimen name="logout_button_margin_bottom">12dp</dimen>
- <dimen name="logout_button_corner_radius">4dp</dimen>
+ <dimen name="logout_button_corner_radius">50dp</dimen>
<!-- Blur radius on status bar window and power menu -->
<dimen name="min_window_blur_radius">1px</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index c2b87a55f366..d442cc51cfd7 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -22,7 +22,7 @@
<bool name="flag_notification_pipeline2_rendering">false</bool>
<bool name="flag_notif_updates">true</bool>
- <bool name="flag_monet">false</bool>
+ <bool name="flag_monet">true</bool>
<!-- AOD/Lockscreen alternate layout -->
<bool name="flag_keyguard_layout">true</bool>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
index e5933e6a9aea..9010d5154156 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
@@ -36,6 +36,13 @@ class UnfoldMoveFromCenterAnimator @JvmOverloads constructor(
* [View.setTranslationY]
*/
private val translationApplier: TranslationApplier = object : TranslationApplier {},
+ /**
+ * Allows to set custom implementation for getting
+ * view location. Could be useful if logical view bounds
+ * are different than actual bounds (e.g. view container may
+ * have larger width than width of the items in the container)
+ */
+ private val viewCenterProvider: ViewCenterProvider = object : ViewCenterProvider {}
) : UnfoldTransitionProgressProvider.TransitionProgressListener {
private val screenSize = Point()
@@ -43,6 +50,8 @@ class UnfoldMoveFromCenterAnimator @JvmOverloads constructor(
private val animatedViews: MutableList<AnimatedView> = arrayListOf()
+ private var lastAnimationProgress: Float = 0f
+
/**
* Updates display properties in order to calculate the initial position for the views
* Must be called before [registerViewForAnimation]
@@ -58,6 +67,19 @@ class UnfoldMoveFromCenterAnimator @JvmOverloads constructor(
}
/**
+ * If target view positions have changed (e.g. because of layout changes) call this method
+ * to re-query view positions and update the translations
+ */
+ fun updateViewPositions() {
+ animatedViews.forEach { animatedView ->
+ animatedView.view.get()?.let {
+ animatedView.updateAnimatedView(it)
+ }
+ }
+ onTransitionProgress(lastAnimationProgress)
+ }
+
+ /**
* Registers a view to be animated, the view should be measured and layouted
* After finishing the animation it is necessary to clear
* the views using [clearRegisteredViews]
@@ -85,45 +107,30 @@ class UnfoldMoveFromCenterAnimator @JvmOverloads constructor(
)
}
}
+ lastAnimationProgress = progress
}
- private fun createAnimatedView(view: View): AnimatedView {
- val viewCenter = getViewCenter(view)
+ private fun createAnimatedView(view: View): AnimatedView =
+ AnimatedView(view = WeakReference(view)).updateAnimatedView(view)
+
+ private fun AnimatedView.updateAnimatedView(view: View): AnimatedView {
+ val viewCenter = Point()
+ viewCenterProvider.getViewCenter(view, viewCenter)
+
val viewCenterX = viewCenter.x
val viewCenterY = viewCenter.y
- val translationX: Float
- val translationY: Float
-
if (isVerticalFold) {
val distanceFromScreenCenterToViewCenter = screenSize.x / 2 - viewCenterX
- translationX = distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE
- translationY = 0f
+ startTranslationX = distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE
+ startTranslationY = 0f
} else {
val distanceFromScreenCenterToViewCenter = screenSize.y / 2 - viewCenterY
- translationX = 0f
- translationY = distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE
+ startTranslationX = 0f
+ startTranslationY = distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE
}
- return AnimatedView(
- view = WeakReference(view),
- startTranslationX = translationX,
- startTranslationY = translationY
- )
- }
-
- private fun getViewCenter(view: View): Point {
- val viewLocation = IntArray(2)
- view.getLocationOnScreen(viewLocation)
-
- val viewX = viewLocation[0]
- val viewY = viewLocation[1]
-
- val outPoint = Point()
- outPoint.x = viewX + view.width / 2
- outPoint.y = viewY + view.height / 2
-
- return outPoint
+ return this
}
/**
@@ -139,10 +146,29 @@ class UnfoldMoveFromCenterAnimator @JvmOverloads constructor(
}
}
+ /**
+ * Interface that allows to use custom logic to get the center of the view
+ */
+ interface ViewCenterProvider {
+ /**
+ * Called when we need to get the center of the view
+ */
+ fun getViewCenter(view: View, outPoint: Point) {
+ val viewLocation = IntArray(2)
+ view.getLocationOnScreen(viewLocation)
+
+ val viewX = viewLocation[0]
+ val viewY = viewLocation[1]
+
+ outPoint.x = viewX + view.width / 2
+ outPoint.y = viewY + view.height / 2
+ }
+ }
+
private class AnimatedView(
val view: WeakReference<View>,
- val startTranslationX: Float,
- val startTranslationY: Float
+ var startTranslationX: Float = 0f,
+ var startTranslationY: Float = 0f
)
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 49cd279fb4be..b6be6edc7a10 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -22,7 +22,6 @@ import android.hardware.SensorManager
import android.hardware.devicestate.DeviceStateManager
import android.os.Handler
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
-import com.android.systemui.unfold.config.ANIMATION_MODE_HINGE_ANGLE
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
@@ -30,7 +29,6 @@ import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgress
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
-import com.android.systemui.unfold.updates.hinge.RotationSensorHingeAngleProvider
import java.lang.IllegalStateException
import java.util.concurrent.Executor
@@ -50,14 +48,8 @@ fun createUnfoldTransitionProgressProvider(
}
val hingeAngleProvider =
- if (config.mode == ANIMATION_MODE_HINGE_ANGLE) {
- // TODO: after removing temporary "config.mode" we should just
- // switch between fixed timing and hinge sensor based on this flag
- if (config.isHingeAngleEnabled) {
- HingeSensorAngleProvider(sensorManager)
- } else {
- RotationSensorHingeAngleProvider(sensorManager)
- }
+ if (config.isHingeAngleEnabled) {
+ HingeSensorAngleProvider(sensorManager)
} else {
EmptyHingeAngleProvider()
}
@@ -70,7 +62,7 @@ fun createUnfoldTransitionProgressProvider(
mainExecutor
)
- return if (config.mode == ANIMATION_MODE_HINGE_ANGLE) {
+ return if (config.isHingeAngleEnabled) {
PhysicsBasedUnfoldTransitionProgressProvider(
mainHandler,
foldStateProvider
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
index e7c6998a847e..3f027e30b473 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
@@ -23,17 +23,16 @@ internal class ResourceUnfoldTransitionConfig(
) : UnfoldTransitionConfig {
override val isEnabled: Boolean
- get() = readIsEnabled() && mode != ANIMATION_MODE_DISABLED
+ get() = readIsEnabledResource() && isPropertyEnabled
override val isHingeAngleEnabled: Boolean
get() = readIsHingeAngleEnabled()
- @AnimationMode
- override val mode: Int
+ private val isPropertyEnabled: Boolean
get() = SystemProperties.getInt(UNFOLD_TRANSITION_MODE_PROPERTY_NAME,
- ANIMATION_MODE_FIXED_TIMING)
+ UNFOLD_TRANSITION_PROPERTY_ENABLED) == UNFOLD_TRANSITION_PROPERTY_ENABLED
- private fun readIsEnabled(): Boolean = context.resources
+ private fun readIsEnabledResource(): Boolean = context.resources
.getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)
private fun readIsHingeAngleEnabled(): Boolean = context.resources
@@ -44,4 +43,5 @@ internal class ResourceUnfoldTransitionConfig(
* Temporary persistent property to control unfold transition mode
* See [com.android.unfold.config.AnimationMode]
*/
-private const val UNFOLD_TRANSITION_MODE_PROPERTY_NAME = "persist.unfold.transition_mode"
+private const val UNFOLD_TRANSITION_MODE_PROPERTY_NAME = "persist.unfold.transition_enabled"
+private const val UNFOLD_TRANSITION_PROPERTY_ENABLED = 1
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
index a5697578e05a..5b187b3486c6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
@@ -15,25 +15,7 @@
*/
package com.android.systemui.unfold.config
-import android.annotation.IntDef
-
interface UnfoldTransitionConfig {
val isEnabled: Boolean
val isHingeAngleEnabled: Boolean
-
- @AnimationMode
- val mode: Int
}
-
-@IntDef(prefix = ["ANIMATION_MODE_"], value = [
- ANIMATION_MODE_DISABLED,
- ANIMATION_MODE_FIXED_TIMING,
- ANIMATION_MODE_HINGE_ANGLE
-])
-
-@Retention(AnnotationRetention.SOURCE)
-annotation class AnimationMode
-
-const val ANIMATION_MODE_DISABLED = 0
-const val ANIMATION_MODE_FIXED_TIMING = 1
-const val ANIMATION_MODE_HINGE_ANGLE = 2
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt
deleted file mode 100644
index 8b6eecfdfde4..000000000000
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.android.systemui.unfold.updates.hinge
-
-import android.hardware.Sensor
-import android.hardware.SensorEvent
-import android.hardware.SensorEventListener
-import android.hardware.SensorManager
-import androidx.core.util.Consumer
-import com.android.systemui.shared.recents.utilities.Utilities
-
-/**
- * Temporary hinge angle provider that uses rotation sensor instead.
- * It requires to have the device in a certain position to work correctly
- * (flat to the ground)
- */
-internal class RotationSensorHingeAngleProvider(
- private val sensorManager: SensorManager
-) : HingeAngleProvider {
-
- private val sensorListener = HingeAngleSensorListener()
- private val listeners: MutableList<Consumer<Float>> = arrayListOf()
-
- override fun start() {
- val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR)
- sensorManager.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_FASTEST)
- }
-
- override fun stop() {
- sensorManager.unregisterListener(sensorListener)
- }
-
- override fun removeCallback(listener: Consumer<Float>) {
- listeners.remove(listener)
- }
-
- override fun addCallback(listener: Consumer<Float>) {
- listeners.add(listener)
- }
-
- private fun onHingeAngle(angle: Float) {
- listeners.forEach { it.accept(angle) }
- }
-
- private inner class HingeAngleSensorListener : SensorEventListener {
-
- override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
- }
-
- override fun onSensorChanged(event: SensorEvent) {
- // Jumbojack sends incorrect sensor reading 1.0f event in the beginning, let's ignore it
- if (event.values[3] == 1.0f) return
-
- val angleRadians = event.values.convertToAngle()
- val hingeAngleDegrees = Math.toDegrees(angleRadians).toFloat()
- val angle = Utilities.clamp(hingeAngleDegrees, FULLY_CLOSED_DEGREES, FULLY_OPEN_DEGREES)
- onHingeAngle(angle)
- }
-
- private val rotationMatrix = FloatArray(9)
- private val resultOrientation = FloatArray(9)
-
- private fun FloatArray.convertToAngle(): Double {
- SensorManager.getRotationMatrixFromVector(rotationMatrix, this)
- SensorManager.getOrientation(rotationMatrix, resultOrientation)
- return resultOrientation[2] + Math.PI
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 92f89d6b90fd..efcf40a66258 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -160,9 +160,7 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie
mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
mBatteryController.removeCallback(mBatteryCallback);
- if (!mView.isAttachedToWindow()) {
- mStatusBarStateController.removeCallback(mStatusBarStatePersistentListener);
- }
+ mStatusBarStateController.removeCallback(mStatusBarStatePersistentListener);
}
/** Animate the clock appearance */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 11c4b6a27968..260b39378485 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -72,13 +72,16 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
* Clock for both small and large sizes
*/
private AnimatableClockController mClockViewController;
- private FrameLayout mClockFrame;
+ private FrameLayout mClockFrame; // top aligned clock
private AnimatableClockController mLargeClockViewController;
- private FrameLayout mLargeClockFrame;
+ private FrameLayout mLargeClockFrame; // centered clock
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardBypassController mBypassController;
+ private int mLargeClockTopMargin = 0;
+ private int mKeyguardClockTopMargin = 0;
+
/**
* Listener for changes to the color palette.
*
@@ -177,6 +180,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
mColorExtractor.addOnColorsChangedListener(mColorsListener);
mView.updateColors(getGradientColors());
+ mKeyguardClockTopMargin =
+ mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
if (mOnlyClock) {
View ksa = mView.findViewById(R.id.keyguard_status_area);
@@ -241,6 +246,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
*/
public void onDensityOrFontScaleChanged() {
mView.onDensityOrFontScaleChanged();
+ mKeyguardClockTopMargin =
+ mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
updateClockLayout();
}
@@ -249,9 +256,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
if (mSmartspaceController.isEnabled()) {
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
MATCH_PARENT);
- lp.topMargin = getContext().getResources().getDimensionPixelSize(
+ mLargeClockTopMargin = getContext().getResources().getDimensionPixelSize(
R.dimen.keyguard_large_clock_top_margin);
+ lp.topMargin = mLargeClockTopMargin;
mLargeClockFrame.setLayoutParams(lp);
+ } else {
+ mLargeClockTopMargin = 0;
}
}
@@ -363,6 +373,28 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
}
+ /**
+ * Get y-bottom position of the currently visible clock on the keyguard.
+ * We can't directly getBottom() because clock changes positions in AOD for burn-in
+ */
+ int getClockBottom(int statusBarHeaderHeight) {
+ if (mLargeClockFrame.getVisibility() == View.VISIBLE) {
+ View clock = mLargeClockFrame.findViewById(
+ com.android.systemui.R.id.animatable_clock_view_large);
+ int frameHeight = mLargeClockFrame.getHeight();
+ int clockHeight = clock.getHeight();
+ return frameHeight / 2 + clockHeight / 2;
+ } else {
+ return mClockFrame.findViewById(
+ com.android.systemui.R.id.animatable_clock_view).getHeight()
+ + statusBarHeaderHeight + mKeyguardClockTopMargin;
+ }
+ }
+
+ boolean isClockTopAligned() {
+ return mLargeClockFrame.getVisibility() != View.VISIBLE;
+ }
+
private void updateAodIcons() {
NotificationIconContainer nic = (NotificationIconContainer)
mView.findViewById(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index a35aedf6f503..1862fc7f6603 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -102,7 +102,7 @@ public class KeyguardPatternView extends KeyguardInputView
ConstraintSet cs = new ConstraintSet();
cs.clone(mContainer);
- cs.setGuidelinePercent(R.id.pin_pad_top_guideline, posture == DEVICE_POSTURE_HALF_OPENED
+ cs.setGuidelinePercent(R.id.pattern_top_guideline, posture == DEVICE_POSTURE_HALF_OPENED
? halfOpenPercentage : 0.0f);
cs.applyTo(mContainer);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 260590685674..64d214d1eee7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -54,6 +54,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.ArrayList;
@@ -472,6 +473,11 @@ public class KeyguardSecurityContainer extends FrameLayout {
mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
: Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
+ int keyguardState = mIsSecurityViewLeftAligned
+ ? SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SWITCH_LEFT
+ : SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SWITCH_RIGHT;
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, keyguardState);
+
updateSecurityViewLocation(animate);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 0df2d65d2acb..9d649e78c363 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -151,9 +151,17 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
+ int bouncerSide = SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__DEFAULT;
+ if (canUseOneHandedBouncer()) {
+ bouncerSide = isOneHandedKeyguardLeftAligned()
+ ? SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__LEFT
+ : SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__RIGHT;
+ }
+
if (success) {
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
- SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
+ SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS,
+ bouncerSide);
mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
// Force a garbage collection in an attempt to erase any lockscreen password left in
// memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
@@ -168,7 +176,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
});
} else {
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
- SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
+ SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE,
+ bouncerSide);
reportFailedUnlockAttempt(userId, timeoutMs);
}
mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
@@ -314,6 +323,14 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
@Override
public void onResume(int reason) {
if (mCurrentSecurityMode != SecurityMode.None) {
+ int state = SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN;
+ if (canUseOneHandedBouncer()) {
+ state = mView.isOneHandedModeLeftAligned()
+ ? SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN_LEFT
+ : SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN_RIGHT;
+ }
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, state);
+
getCurrentSecurityController().onResume(reason);
}
mView.onResume(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 8bf8e0926095..d23b1c88b345 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -187,6 +187,20 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
/**
+ * Get y-bottom position of the currently visible clock.
+ */
+ public int getClockBottom(int statusBarHeaderHeight) {
+ return mKeyguardClockSwitchController.getClockBottom(statusBarHeaderHeight);
+ }
+
+ /**
+ * @return true if the currently displayed clock is top aligned (as opposed to center aligned)
+ */
+ public boolean isClockTopAligned() {
+ return mKeyguardClockSwitchController.isClockTopAligned();
+ }
+
+ /**
* Set whether the view accessibility importance mode.
*/
public void setStatusAccessibilityImportance(int mode) {
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index dd3bb8990599..371564a98aad 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -45,7 +45,7 @@ public class LockIconView extends FrameLayout implements Dumpable {
private int mRadius;
private ImageView mLockIcon;
- private ImageView mUnlockBgView;
+ private ImageView mBgView;
private int mLockIconColor;
@@ -58,19 +58,19 @@ public class LockIconView extends FrameLayout implements Dumpable {
public void onFinishInflate() {
super.onFinishInflate();
mLockIcon = findViewById(R.id.lock_icon);
- mUnlockBgView = findViewById(R.id.lock_icon_bg);
+ mBgView = findViewById(R.id.lock_icon_bg);
}
void updateColorAndBackgroundVisibility(boolean useBackground) {
- if (useBackground) {
+ if (useBackground && mLockIcon.getDrawable() != null) {
mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
android.R.attr.textColorPrimary);
- mUnlockBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
- mUnlockBgView.setVisibility(View.VISIBLE);
+ mBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
+ mBgView.setVisibility(View.VISIBLE);
} else {
mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
R.attr.wallpaperTextColorAccent);
- mUnlockBgView.setVisibility(View.GONE);
+ mBgView.setVisibility(View.GONE);
}
mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor));
@@ -78,6 +78,11 @@ public class LockIconView extends FrameLayout implements Dumpable {
void setImageDrawable(Drawable drawable) {
mLockIcon.setImageDrawable(drawable);
+ if (drawable == null) {
+ mBgView.setVisibility(View.INVISIBLE);
+ } else {
+ mBgView.setVisibility(View.VISIBLE);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 2a4022ca57b6..28e19acce506 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -19,6 +19,8 @@ package com.android.keyguard;
import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static com.android.systemui.classifier.Classifier.LOCK_ICON;
+import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
+import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInProgressOffset;
import android.content.Context;
import android.content.res.Configuration;
@@ -27,11 +29,13 @@ import android.graphics.Rect;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.media.AudioAttributes;
import android.os.Process;
import android.os.Vibrator;
import android.util.DisplayMetrics;
+import android.util.MathUtils;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
@@ -59,6 +63,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.airbnb.lottie.LottieAnimationView;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Objects;
@@ -94,6 +100,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
@NonNull private final DelayableExecutor mExecutor;
private boolean mUdfpsEnrolled;
+ @NonNull private LottieAnimationView mAodFp;
+
@NonNull private final AnimatedVectorDrawable mFpToUnlockIcon;
@NonNull private final AnimatedVectorDrawable mLockToUnlockIcon;
@NonNull private final Drawable mLockIcon;
@@ -112,6 +120,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
private boolean mIsKeyguardShowing;
private boolean mUserUnlockedWithBiometric;
private Runnable mCancelDelayedUpdateVisibilityRunnable;
+ private Runnable mOnGestureDetectedRunnable;
private boolean mUdfpsSupported;
private float mHeightPixels;
@@ -121,6 +130,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
private boolean mShowUnlockIcon;
private boolean mShowLockIcon;
+ // for udfps when strong auth is required or unlocked on AOD
+ private boolean mShowAODFpIcon;
+ private final int mMaxBurnInOffsetX;
+ private final int mMaxBurnInOffsetY;
+ private float mInterpolatedDarkAmount;
+
private boolean mDownDetected;
private boolean mDetectedLongPress;
private final Rect mSensorTouchLocation = new Rect();
@@ -155,6 +170,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mAuthRippleController = authRippleController;
final Context context = view.getContext();
+ mAodFp = mView.findViewById(R.id.lock_udfps_aod_fp);
+ mMaxBurnInOffsetX = context.getResources()
+ .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
+ mMaxBurnInOffsetY = context.getResources()
+ .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
+
mUnlockIcon = mView.getContext().getResources().getDrawable(
R.drawable.ic_unlock,
mView.getContext().getTheme());
@@ -173,19 +194,19 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
@Override
protected void onInit() {
- mAuthController.addCallback(mAuthControllerCallback);
- mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
-
mView.setAccessibilityDelegate(mAccessibilityDelegate);
}
@Override
protected void onViewAttached() {
+ updateIsUdfpsEnrolled();
updateConfiguration();
updateKeyguardShowing();
mUserUnlockedWithBiometric = false;
+
mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
mIsDozing = mStatusBarStateController.isDozing();
+ mInterpolatedDarkAmount = mStatusBarStateController.getDozeAmount();
mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
mStatusBarState = mStatusBarStateController.getState();
@@ -193,15 +214,18 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
updateColors();
mConfigurationController.addCallback(mConfigurationListener);
+ mAuthController.addCallback(mAuthControllerCallback);
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
mStatusBarStateController.addCallback(mStatusBarStateListener);
mKeyguardStateController.addCallback(mKeyguardStateCallback);
mDownDetected = false;
+ updateBurnInOffsets();
updateVisibility();
}
@Override
protected void onViewDetached() {
+ mAuthController.removeCallback(mAuthControllerCallback);
mConfigurationController.removeCallback(mConfigurationListener);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
@@ -231,7 +255,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mCancelDelayedUpdateVisibilityRunnable = null;
}
- if (!mIsKeyguardShowing) {
+ if (!mIsKeyguardShowing && !mIsDozing) {
mView.setVisibility(View.INVISIBLE);
return;
}
@@ -242,6 +266,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen()
&& (!mUdfpsEnrolled || !mRunningFPS);
mShowUnlockIcon = mCanDismissLockScreen && isLockScreen();
+ mShowAODFpIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS;
final CharSequence prevContentDescription = mView.getContentDescription();
if (mShowLockIcon) {
@@ -264,10 +289,22 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
}
mView.setVisibility(View.VISIBLE);
mView.setContentDescription(mUnlockedLabel);
+ } else if (mShowAODFpIcon) {
+ mView.setImageDrawable(null);
+ mView.setContentDescription(null);
+ mAodFp.setVisibility(View.VISIBLE);
+ mAodFp.setContentDescription(mCanDismissLockScreen ? mUnlockedLabel : mLockedLabel);
+ mView.setVisibility(View.VISIBLE);
} else {
mView.setVisibility(View.INVISIBLE);
mView.setContentDescription(null);
}
+
+ if (!mShowAODFpIcon) {
+ mAodFp.setVisibility(View.INVISIBLE);
+ mAodFp.setContentDescription(null);
+ }
+
if (!Objects.equals(prevContentDescription, mView.getContentDescription())
&& mView.getContentDescription() != null) {
mView.announceForAccessibility(mView.getContentDescription());
@@ -330,8 +367,9 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
private void updateLockIconLocation() {
if (mUdfpsSupported) {
FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0);
- mView.setCenterLocation(new PointF(props.sensorLocationX, props.sensorLocationY),
- props.sensorRadius);
+ final SensorLocationInternal location = props.getLocation();
+ mView.setCenterLocation(new PointF(location.sensorLocationX, location.sensorLocationY),
+ location.sensorRadius);
} else {
mView.setCenterLocation(
new PointF(mWidthPixels / 2,
@@ -344,10 +382,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("mUdfpsSupported: " + mUdfpsSupported);
pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled);
pw.println("mIsKeyguardShowing: " + mIsKeyguardShowing);
pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
pw.println(" mShowLockIcon: " + mShowLockIcon);
+ pw.println(" mShowAODFpIcon: " + mShowAODFpIcon);
pw.println(" mIsDozing: " + mIsDozing);
pw.println(" mIsBouncerShowing: " + mIsBouncerShowing);
pw.println(" mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric);
@@ -355,17 +395,57 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen);
pw.println(" mStatusBarState: " + StatusBarState.toShortString(mStatusBarState));
pw.println(" mQsExpanded: " + mQsExpanded);
+ pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount);
if (mView != null) {
mView.dump(fd, pw, args);
}
}
+ /** Every minute, update the aod icon's burn in offset */
+ public void dozeTimeTick() {
+ updateBurnInOffsets();
+ }
+
+ private void updateBurnInOffsets() {
+ float offsetX = MathUtils.lerp(0f,
+ getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
+ - mMaxBurnInOffsetX, mInterpolatedDarkAmount);
+ float offsetY = MathUtils.lerp(0f,
+ getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
+ - mMaxBurnInOffsetY, mInterpolatedDarkAmount);
+ float progress = MathUtils.lerp(0f, getBurnInProgressOffset(), mInterpolatedDarkAmount);
+
+ mAodFp.setTranslationX(offsetX);
+ mAodFp.setTranslationY(offsetY);
+ mAodFp.setProgress(progress);
+ mAodFp.setAlpha(255 * mInterpolatedDarkAmount);
+ }
+
+ private void updateIsUdfpsEnrolled() {
+ boolean wasUdfpsSupported = mUdfpsSupported;
+ boolean wasUdfpsEnrolled = mUdfpsEnrolled;
+
+ mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
+ mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
+ if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) {
+ updateVisibility();
+ }
+ }
+
private StatusBarStateController.StateListener mStatusBarStateListener =
new StatusBarStateController.StateListener() {
@Override
+ public void onDozeAmountChanged(float linear, float eased) {
+ mInterpolatedDarkAmount = eased;
+ updateBurnInOffsets();
+ }
+
+ @Override
public void onDozingChanged(boolean isDozing) {
mIsDozing = isDozing;
+ updateBurnInOffsets();
+ updateIsUdfpsEnrolled();
updateVisibility();
}
@@ -439,7 +519,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
KeyguardUpdateMonitor.getCurrentUser());
}
- mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
+ updateIsUdfpsEnrolled();
updateVisibility();
}
@@ -485,8 +565,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
// intercept all following touches until we see MotionEvent.ACTION_CANCEL UP or
// MotionEvent.ACTION_UP (see #onTouchEvent)
- mDownDetected = true;
- if (mVibrator != null) {
+ if (mVibrator != null && !mDownDetected) {
mVibrator.vibrate(
Process.myUid(),
getContext().getOpPackageName(),
@@ -494,6 +573,8 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
"lockIcon-onDown",
VIBRATION_SONIFICATION_ATTRIBUTES);
}
+
+ mDownDetected = true;
return true;
}
@@ -551,6 +632,9 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
mAuthRippleController.showRipple(FINGERPRINT);
}
updateVisibility();
+ if (mOnGestureDetectedRunnable != null) {
+ mOnGestureDetectedRunnable.run();
+ }
mKeyguardViewController.showBouncer(/* scrim */ true);
return true;
}
@@ -561,16 +645,18 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
* in a 'clickable' state
* @return whether to intercept the touch event
*/
- public boolean onTouchEvent(MotionEvent event) {
+ public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) {
if (mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
- && mView.getVisibility() == View.VISIBLE) {
+ && (mView.getVisibility() == View.VISIBLE
+ || mAodFp.getVisibility() == View.VISIBLE)) {
+ mOnGestureDetectedRunnable = onGestureDetectedRunnable;
mGestureDetector.onTouchEvent(event);
}
// we continue to intercept all following touches until we see MotionEvent.ACTION_CANCEL UP
// or MotionEvent.ACTION_UP. this is to avoid passing the touch to NPV
// after the lock icon disappears on device entry
- if (mDownDetected && mDetectedLongPress) {
+ if (mDownDetected) {
if (event.getAction() == MotionEvent.ACTION_CANCEL
|| event.getAction() == MotionEvent.ACTION_UP) {
mDownDetected = false;
@@ -594,7 +680,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
@Override
public void onAllAuthenticatorsRegistered() {
- mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
+ updateIsUdfpsEnrolled();
updateConfiguration();
}
};
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
index 3a0357d2a284..4331f52eb1a2 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardQsUserSwitchComponent.java
@@ -16,7 +16,8 @@
package com.android.keyguard.dagger;
-import com.android.systemui.statusbar.phone.UserAvatarView;
+import android.widget.FrameLayout;
+
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
import dagger.BindsInstance;
@@ -31,8 +32,7 @@ public interface KeyguardQsUserSwitchComponent {
/** Simple factory for {@link KeyguardUserSwitcherComponent}. */
@Subcomponent.Factory
interface Factory {
- KeyguardQsUserSwitchComponent build(
- @BindsInstance UserAvatarView userAvatarView);
+ KeyguardQsUserSwitchComponent build(@BindsInstance FrameLayout userAvatarContainer);
}
/** Builds a {@link KeyguardQsUserSwitchController}. */
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index 186917fc8807..d325b92cf89f 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -16,18 +16,13 @@
package com.android.systemui;
-import static android.os.PowerManager.WAKE_REASON_UNKNOWN;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Build;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
@@ -48,20 +43,15 @@ public class LatencyTester extends SystemUI {
private static final String
ACTION_FACE_WAKE =
"com.android.systemui.latency.ACTION_FACE_WAKE";
- private static final String
- ACTION_TURN_ON_SCREEN =
- "com.android.systemui.latency.ACTION_TURN_ON_SCREEN";
private final BiometricUnlockController mBiometricUnlockController;
- private final PowerManager mPowerManager;
private final BroadcastDispatcher mBroadcastDispatcher;
@Inject
public LatencyTester(Context context, BiometricUnlockController biometricUnlockController,
- PowerManager powerManager, BroadcastDispatcher broadcastDispatcher) {
+ BroadcastDispatcher broadcastDispatcher) {
super(context);
mBiometricUnlockController = biometricUnlockController;
- mPowerManager = powerManager;
mBroadcastDispatcher = broadcastDispatcher;
}
@@ -74,7 +64,6 @@ public class LatencyTester extends SystemUI {
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_FINGERPRINT_WAKE);
filter.addAction(ACTION_FACE_WAKE);
- filter.addAction(ACTION_TURN_ON_SCREEN);
mBroadcastDispatcher.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -83,22 +72,11 @@ public class LatencyTester extends SystemUI {
fakeWakeAndUnlock(BiometricSourceType.FINGERPRINT);
} else if (ACTION_FACE_WAKE.equals(action)) {
fakeWakeAndUnlock(BiometricSourceType.FACE);
- } else if (ACTION_TURN_ON_SCREEN.equals(action)) {
- fakeTurnOnScreen();
}
}
}, filter);
}
- private void fakeTurnOnScreen() {
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionStart(
- LatencyTracker.ACTION_TURN_ON_SCREEN);
- }
- mPowerManager.wakeUp(
- SystemClock.uptimeMillis(), WAKE_REASON_UNKNOWN, "android.policy:LATENCY_TESTS");
- }
-
private void fakeWakeAndUnlock(BiometricSourceType type) {
mBiometricUnlockController.onBiometricAcquired(type);
mBiometricUnlockController.onBiometricAuthenticated(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 0790af94f287..71445a7c2cfe 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -114,7 +114,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
@VisibleForTesting
IBiometricSysuiReceiver mReceiver;
@VisibleForTesting
- @NonNull final BiometricOrientationEventListener mOrientationListener;
+ @NonNull final BiometricDisplayListener mOrientationListener;
@Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
@Nullable private List<FingerprintSensorPropertiesInternal> mFpProps;
@Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps;
@@ -459,13 +459,15 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
mSidefpsControllerFactory = sidefpsControllerFactory;
mWindowManager = windowManager;
mUdfpsEnrolledForUser = new SparseBooleanArray();
- mOrientationListener = new BiometricOrientationEventListener(context,
+ mOrientationListener = new BiometricDisplayListener(
+ context,
+ displayManager,
+ handler,
+ BiometricDisplayListener.SensorType.Generic.INSTANCE,
() -> {
onOrientationChanged();
return Unit.INSTANCE;
- },
- displayManager,
- handler);
+ });
mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 9c818fff5018..ba64195ea78b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics
+import android.animation.ValueAnimator
import android.content.Context
import android.content.res.Configuration
import android.graphics.PointF
@@ -26,6 +27,8 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils
import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -36,10 +39,14 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.ViewController
import java.io.PrintWriter
import javax.inject.Inject
import javax.inject.Provider
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+
+private const val WAKE_AND_UNLOCK_FADE_DURATION = 180L
/***
* Controls the ripple effect that shows when authentication is successful.
@@ -52,20 +59,30 @@ class AuthRippleController @Inject constructor(
private val authController: AuthController,
private val configurationController: ConfigurationController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val keyguardStateController: KeyguardStateController,
+ private val wakefulnessLifecycle: WakefulnessLifecycle,
private val commandRegistry: CommandRegistry,
private val notificationShadeWindowController: NotificationShadeWindowController,
private val bypassController: KeyguardBypassController,
private val biometricUnlockController: BiometricUnlockController,
private val udfpsControllerProvider: Provider<UdfpsController>,
+ private val statusBarStateController: StatusBarStateController,
rippleView: AuthRippleView?
-) : ViewController<AuthRippleView>(rippleView) {
+) : ViewController<AuthRippleView>(rippleView), KeyguardStateController.Callback,
+ WakefulnessLifecycle.Observer {
+
+ @VisibleForTesting
+ internal var startLightRevealScrimOnKeyguardFadingAway = false
var fingerprintSensorLocation: PointF? = null
private var faceSensorLocation: PointF? = null
private var circleReveal: LightRevealEffect? = null
private var udfpsController: UdfpsController? = null
+
private var dwellScale = 2f
private var expandedDwellScale = 2.5f
+ private var aodDwellScale = 1.9f
+ private var aodExpandedDwellScale = 2.3f
private var udfpsRadius: Float = -1f
override fun onInit() {
@@ -82,6 +99,8 @@ class AuthRippleController @Inject constructor(
udfpsController?.addCallback(udfpsControllerCallback)
configurationController.addCallback(configurationChangedListener)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
+ keyguardStateController.addCallback(this)
+ wakefulnessLifecycle.addObserver(this)
commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
}
@@ -91,6 +110,8 @@ class AuthRippleController @Inject constructor(
authController.removeCallback(authControllerCallback)
keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
configurationController.removeCallback(configurationChangedListener)
+ keyguardStateController.removeCallback(this)
+ wakefulnessLifecycle.removeObserver(this)
commandRegistry.unregisterCommand("auth-ripple")
notificationShadeWindowController.setForcePluginOpen(false, this)
@@ -118,30 +139,48 @@ class AuthRippleController @Inject constructor(
private fun showUnlockedRipple() {
notificationShadeWindowController.setForcePluginOpen(true, this)
- val biometricUnlockMode = biometricUnlockController.mode
- val useCircleReveal = circleReveal != null &&
- (biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK ||
- biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING ||
- biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
+ val useCircleReveal = circleReveal != null && biometricUnlockController.isWakeAndUnlock
val lightRevealScrim = statusBar.lightRevealScrim
if (useCircleReveal) {
lightRevealScrim?.revealEffect = circleReveal!!
+ startLightRevealScrimOnKeyguardFadingAway = true
}
mView.startUnlockedRipple(
/* end runnable */
Runnable {
notificationShadeWindowController.setForcePluginOpen(false, this)
- },
- /* circleReveal */
- if (useCircleReveal) {
- lightRevealScrim
- } else {
- null
}
)
}
+ override fun onKeyguardFadingAwayChanged() {
+ if (keyguardStateController.isKeyguardFadingAway) {
+ val lightRevealScrim = statusBar.lightRevealScrim
+ if (startLightRevealScrimOnKeyguardFadingAway && lightRevealScrim != null) {
+ val revealAnimator = ValueAnimator.ofFloat(.1f, 1f).apply {
+ interpolator = Interpolators.LINEAR_OUT_SLOW_IN
+ duration = RIPPLE_ANIMATION_DURATION
+ startDelay = keyguardStateController.keyguardFadingAwayDelay
+ addUpdateListener { animator ->
+ if (lightRevealScrim.revealEffect != circleReveal) {
+ // if the something else took over the reveal, let's do nothing.
+ return@addUpdateListener
+ }
+ lightRevealScrim.revealAmount = animator.animatedValue as Float
+ }
+ }
+ revealAnimator.start()
+ startLightRevealScrimOnKeyguardFadingAway = false
+ }
+ }
+ }
+
+ override fun onStartedGoingToSleep() {
+ // reset the light reveal start in case we were pending an unlock
+ startLightRevealScrimOnKeyguardFadingAway = false
+ }
+
fun updateSensorLocation() {
fingerprintSensorLocation = authController.fingerprintSensorLocation
faceSensorLocation = authController.faceAuthSensorLocation
@@ -163,6 +202,22 @@ class AuthRippleController @Inject constructor(
Utils.getColorAttr(sysuiContext, android.R.attr.colorAccent).defaultColor)
}
+ private fun showDwellRipple() {
+ if (statusBarStateController.isDozing) {
+ mView.startDwellRipple(
+ /* startRadius */ udfpsRadius,
+ /* endRadius */ udfpsRadius * aodDwellScale,
+ /* expandedRadius */ udfpsRadius * aodExpandedDwellScale,
+ /* isDozing */ true)
+ } else {
+ mView.startDwellRipple(
+ /* startRadius */ udfpsRadius,
+ /* endRadius */ udfpsRadius * dwellScale,
+ /* expandedRadius */ udfpsRadius * expandedDwellScale,
+ /* isDozing */ false)
+ }
+ }
+
private val keyguardUpdateMonitorCallback =
object : KeyguardUpdateMonitorCallback() {
override fun onBiometricAuthenticated(
@@ -204,10 +259,7 @@ class AuthRippleController @Inject constructor(
}
mView.setSensorLocation(fingerprintSensorLocation!!)
- mView.startDwellRipple(
- /* startRadius */ udfpsRadius,
- /* endRadius */ udfpsRadius * dwellScale,
- /* expandedRadius */ udfpsRadius * expandedDwellScale)
+ showDwellRipple()
}
override fun onFingerUp() {
@@ -223,7 +275,7 @@ class AuthRippleController @Inject constructor(
private fun updateUdfpsDependentParams() {
authController.udfpsProps?.let {
if (it.size > 0) {
- udfpsRadius = it[0].sensorRadius.toFloat()
+ udfpsRadius = it[0].location.sensorRadius.toFloat()
udfpsController = udfpsControllerProvider.get()
if (mView.isAttachedToWindow) {
@@ -234,14 +286,18 @@ class AuthRippleController @Inject constructor(
}
inner class AuthRippleCommand : Command {
- fun printDwellInfo(pw: PrintWriter) {
- pw.println("dwell ripple: " +
+ fun printLockScreenDwellInfo(pw: PrintWriter) {
+ pw.println("lock screen dwell ripple: " +
"\n\tsensorLocation=$fingerprintSensorLocation" +
"\n\tdwellScale=$dwellScale" +
- "\n\tdwellAlpha=${mView.dwellAlpha}, " +
- "duration=${mView.dwellAlphaDuration}" +
- "\n\tdwellExpand=$expandedDwellScale" +
- "\n\t(crash systemui to reset to default)")
+ "\n\tdwellExpand=$expandedDwellScale")
+ }
+
+ fun printAodDwellInfo(pw: PrintWriter) {
+ pw.println("aod dwell ripple: " +
+ "\n\tsensorLocation=$fingerprintSensorLocation" +
+ "\n\tdwellScale=$aodDwellScale" +
+ "\n\tdwellExpand=$aodExpandedDwellScale")
}
override fun execute(pw: PrintWriter, args: List<String>) {
@@ -249,40 +305,12 @@ class AuthRippleController @Inject constructor(
invalidCommand(pw)
} else {
when (args[0]) {
- "dwellScale" -> {
- if (args.size > 1 && args[1].toFloatOrNull() != null) {
- dwellScale = args[1].toFloat()
- printDwellInfo(pw)
- } else {
- pw.println("expected float argument <dwellScale>")
- }
- }
- "dwellAlpha" -> {
- if (args.size > 2 && args[1].toFloatOrNull() != null &&
- args[2].toLongOrNull() != null) {
- mView.dwellAlpha = args[1].toFloat()
- if (args[2].toFloat() > 200L) {
- pw.println("alpha animation duration must be less than 200ms.")
- }
- mView.dwellAlphaDuration = kotlin.math.min(args[2].toLong(), 200L)
- printDwellInfo(pw)
+ "dwell" -> {
+ showDwellRipple()
+ if (statusBarStateController.isDozing) {
+ printAodDwellInfo(pw)
} else {
- pw.println("expected two float arguments:" +
- " <dwellAlpha> <dwellAlphaDuration>")
- }
- }
- "dwellExpand" -> {
- if (args.size > 1 && args[1].toFloatOrNull() != null) {
- val expandedScale = args[1].toFloat()
- if (expandedScale <= dwellScale) {
- pw.println("invalid expandedScale. must be greater than " +
- "dwellScale=$dwellScale, but given $expandedScale")
- } else {
- expandedDwellScale = expandedScale
- }
- printDwellInfo(pw)
- } else {
- pw.println("expected float argument <expandedScale>")
+ printLockScreenDwellInfo(pw)
}
}
"fingerprint" -> {
@@ -313,9 +341,7 @@ class AuthRippleController @Inject constructor(
override fun help(pw: PrintWriter) {
pw.println("Usage: adb shell cmd statusbar auth-ripple <command>")
pw.println("Available commands:")
- pw.println(" dwellScale <200ms_scale: float>")
- pw.println(" dwellAlpha <alpha: float> <duration : long>")
- pw.println(" dwellExpand <expanded_scale: float>")
+ pw.println(" dwell")
pw.println(" fingerprint")
pw.println(" face")
pw.println(" custom <x-location: int> <y-location: int>")
@@ -326,4 +352,8 @@ class AuthRippleController @Inject constructor(
help(pw)
}
}
+
+ companion object {
+ const val RIPPLE_ANIMATION_DURATION: Long = 1533
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 8e1303713171..c6d26ffb9957 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -26,13 +26,10 @@ import android.graphics.PointF
import android.util.AttributeSet
import android.view.View
import android.view.animation.PathInterpolator
-import com.android.internal.R.attr.interpolator
import com.android.internal.graphics.ColorUtils
import com.android.systemui.animation.Interpolators
-import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.charging.RippleShader
-private const val RIPPLE_ANIMATION_DURATION: Long = 1533
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
/**
@@ -45,12 +42,18 @@ private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
*/
class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private val retractInterpolator = PathInterpolator(.05f, .93f, .1f, 1f)
- private val dwellPulseDuration = 200L
- var dwellAlphaDuration = dwellPulseDuration
+
+ private val dwellPulseDuration = 50L
+ private val dwellAlphaDuration = dwellPulseDuration
+ private val dwellAlpha: Float = 1f
private val dwellExpandDuration = 1200L - dwellPulseDuration
- private val retractDuration = 400L
- var dwellAlpha: Float = .5f
+ private val aodDwellPulseDuration = 50L
+ private var aodDwellAlphaDuration = aodDwellPulseDuration
+ private var aodDwellAlpha: Float = .8f
+ private var aodDwellExpandDuration = 1200L - aodDwellPulseDuration
+
+ private val retractDuration = 400L
private var alphaInDuration: Long = 0
private var unlockedRippleInProgress: Boolean = false
private val rippleShader = RippleShader()
@@ -142,7 +145,12 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
* Ripple that moves animates from an outer ripple ring of
* startRadius => endRadius => expandedRadius
*/
- fun startDwellRipple(startRadius: Float, endRadius: Float, expandedRadius: Float) {
+ fun startDwellRipple(
+ startRadius: Float,
+ endRadius: Float,
+ expandedRadius: Float,
+ isDozing: Boolean
+ ) {
if (unlockedRippleInProgress || dwellPulseOutAnimator?.isRunning == true) {
return
}
@@ -153,12 +161,13 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
val endInitialDwellProgress = endRadius / radius / 4f
val endExpandDwellProgress = expandedRadius / radius / 4f
- val pulseOutEndAlpha = (255 * dwellAlpha).toInt()
- val expandDwellEndAlpha = kotlin.math.min((255 * (dwellAlpha + .25f)).toInt(), 255)
+ val alpha = if (isDozing) aodDwellAlpha else dwellAlpha
+ val pulseOutEndAlpha = (255 * alpha).toInt()
+ val expandDwellEndAlpha = kotlin.math.min((255 * (alpha + .25f)).toInt(), 255)
val dwellPulseOutRippleAnimator = ValueAnimator.ofFloat(startDwellProgress,
endInitialDwellProgress).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
- duration = dwellPulseDuration
+ duration = if (isDozing) aodDwellPulseDuration else dwellPulseDuration
addUpdateListener { animator ->
val now = animator.currentPlayTime
rippleShader.progress = animator.animatedValue as Float
@@ -170,7 +179,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
val dwellPulseOutAlphaAnimator = ValueAnimator.ofInt(0, pulseOutEndAlpha).apply {
interpolator = Interpolators.LINEAR
- duration = dwellAlphaDuration
+ duration = if (isDozing) aodDwellAlphaDuration else dwellAlphaDuration
addUpdateListener { animator ->
rippleShader.color = ColorUtils.setAlphaComponent(
rippleShader.color,
@@ -184,7 +193,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
val expandDwellRippleAnimator = ValueAnimator.ofFloat(endInitialDwellProgress,
endExpandDwellProgress).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
- duration = dwellExpandDuration
+ duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
addUpdateListener { animator ->
val now = animator.currentPlayTime
rippleShader.progress = animator.animatedValue as Float
@@ -197,7 +206,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
val expandDwellAlphaAnimator = ValueAnimator.ofInt(pulseOutEndAlpha, expandDwellEndAlpha)
.apply {
interpolator = Interpolators.LINEAR
- duration = dwellExpandDuration
+ duration = if (isDozing) aodDwellExpandDuration else dwellExpandDuration
addUpdateListener { animator ->
rippleShader.color = ColorUtils.setAlphaComponent(
rippleShader.color,
@@ -238,7 +247,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
/**
* Ripple that bursts outwards from the position of the sensor to the edges of the screen
*/
- fun startUnlockedRipple(onAnimationEnd: Runnable?, lightReveal: LightRevealScrim?) {
+ fun startUnlockedRipple(onAnimationEnd: Runnable?) {
if (unlockedRippleInProgress) {
return // Ignore if ripple effect is already playing
}
@@ -254,7 +263,7 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
val rippleAnimator = ValueAnimator.ofFloat(rippleStart, 1f).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
- duration = RIPPLE_ANIMATION_DURATION
+ duration = AuthRippleController.RIPPLE_ANIMATION_DURATION
addUpdateListener { animator ->
val now = animator.currentPlayTime
rippleShader.progress = animator.animatedValue as Float
@@ -264,14 +273,6 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
}
}
- val revealAnimator = ValueAnimator.ofFloat(.1f, 1f).apply {
- interpolator = rippleAnimator.interpolator
- duration = rippleAnimator.duration
- addUpdateListener { animator ->
- lightReveal?.revealAmount = animator.animatedValue as Float
- }
- }
-
val alphaInAnimator = ValueAnimator.ofInt(0, 255).apply {
duration = alphaDuration
addUpdateListener { animator ->
@@ -286,7 +287,6 @@ class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, at
val animatorSet = AnimatorSet().apply {
playTogether(
rippleAnimator,
- revealAnimator,
alphaInAnimator
)
addListener(object : AnimatorListenerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt
new file mode 100644
index 000000000000..b7404dfeb1cc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.content.Context
+import android.hardware.display.DisplayManager
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.os.Handler
+import android.view.Surface
+import com.android.systemui.biometrics.BiometricDisplayListener.SensorType.Generic
+
+/**
+ * A listener for keeping overlays for biometric sensors aligned with the physical device
+ * device's screen. The [onChanged] will be dispatched on the [handler]
+ * whenever a relevant change to the device's configuration (orientation, fold, display change,
+ * etc.) may require the UI to change for the given [sensorType].
+ */
+class BiometricDisplayListener(
+ private val context: Context,
+ private val displayManager: DisplayManager,
+ private val handler: Handler,
+ private val sensorType: SensorType = SensorType.Generic,
+ private val onChanged: () -> Unit
+) : DisplayManager.DisplayListener {
+
+ private var lastRotation = context.display?.rotation ?: Surface.ROTATION_0
+
+ override fun onDisplayAdded(displayId: Int) {}
+ override fun onDisplayRemoved(displayId: Int) {}
+ override fun onDisplayChanged(displayId: Int) {
+ val rotationChanged = didRotationChange()
+
+ when (sensorType) {
+ is SensorType.SideFingerprint -> onChanged()
+ else -> {
+ if (rotationChanged) {
+ onChanged()
+ }
+ }
+ }
+ }
+
+ private fun didRotationChange(): Boolean {
+ val rotation = context.display?.rotation ?: return false
+ val last = lastRotation
+ lastRotation = rotation
+ return last != rotation
+ }
+
+ /** Listen for changes. */
+ fun enable() {
+ displayManager.registerDisplayListener(this, handler)
+ }
+
+ /** Stop listening for changes. */
+ fun disable() {
+ displayManager.unregisterDisplayListener(this)
+ }
+
+ /**
+ * Type of sensor to determine what kind of display changes require layouts.
+ *
+ * The [Generic] type should be used in cases where the modality can vary, such as
+ * biometric prompt (and this object will likely change as multi-mode auth is added).
+ */
+ sealed class SensorType {
+ object Generic : SensorType()
+ data class UnderDisplayFingerprint(
+ val properties: FingerprintSensorPropertiesInternal
+ ) : SensorType()
+ data class SideFingerprint(
+ val properties: FingerprintSensorPropertiesInternal
+ ) : SensorType()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt
deleted file mode 100644
index 98a03a1c444b..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricOrientationEventListener.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.content.Context
-import android.hardware.display.DisplayManager
-import android.os.Handler
-import android.view.OrientationEventListener
-import android.view.Surface
-
-/**
- * An [OrientationEventListener] that invokes the [onOrientationChanged] callback whenever
- * the orientation of the device has changed in order to keep overlays for biometric sensors
- * aligned with the device's screen.
- */
-class BiometricOrientationEventListener(
- private val context: Context,
- private val onOrientationChanged: () -> Unit,
- private val displayManager: DisplayManager,
- private val handler: Handler
-) : DisplayManager.DisplayListener {
-
- private var lastRotation = context.display?.rotation ?: Surface.ROTATION_0
-
- override fun onDisplayAdded(displayId: Int) {}
- override fun onDisplayRemoved(displayId: Int) {}
- override fun onDisplayChanged(displayId: Int) {
- val rotation = context.display?.rotation ?: return
- if (lastRotation != rotation) {
- lastRotation = rotation
-
- onOrientationChanged()
- }
- }
-
- fun enable() {
- displayManager.registerDisplayListener(this, handler)
- }
-
- fun disable() {
- displayManager.unregisterDisplayListener(this)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS b/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS
index 947466f3baaf..adb10f01b5e1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/OWNERS
@@ -1,3 +1,4 @@
set noparent
include /services/core/java/com/android/server/biometrics/OWNERS
+beverlyt@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java
deleted file mode 100644
index 8f6e2498a00b..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.hardware.display.DisplayManager;
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.hardware.fingerprint.ISidefpsController;
-import android.os.Handler;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.Surface;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import javax.inject.Inject;
-
-import kotlin.Unit;
-
-/**
- * Shows and hides the side fingerprint sensor (side-fps) overlay and handles side fps touch events.
- */
-@SysUISingleton
-public class SidefpsController {
- private static final String TAG = "SidefpsController";
- @NonNull private final Context mContext;
- @NonNull private final LayoutInflater mInflater;
- private final FingerprintManager mFingerprintManager;
- private final WindowManager mWindowManager;
- private final DelayableExecutor mFgExecutor;
- @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;
-
- // TODO: update mDisplayHeight and mDisplayWidth for multi-display devices
- private final int mDisplayHeight;
- private final int mDisplayWidth;
-
- private boolean mIsVisible = false;
- @Nullable private SidefpsView mView;
-
- static final int SFPS_AFFORDANCE_WIDTH = 50; // in default portrait mode
-
- @NonNull
- private final ISidefpsController mSidefpsControllerImpl = new ISidefpsController.Stub() {
- @Override
- public void show() {
- mFgExecutor.execute(() -> {
- SidefpsController.this.show();
- mIsVisible = true;
- });
- }
-
- @Override
- public void hide() {
- mFgExecutor.execute(() -> {
- SidefpsController.this.hide();
- mIsVisible = false;
- });
- }
- };
-
- @VisibleForTesting
- final FingerprintSensorPropertiesInternal mSensorProps;
- private final WindowManager.LayoutParams mCoreLayoutParams;
-
- @Inject
- public SidefpsController(@NonNull Context context,
- @NonNull LayoutInflater inflater,
- @Nullable FingerprintManager fingerprintManager,
- @NonNull WindowManager windowManager,
- @Main DelayableExecutor fgExecutor,
- @NonNull DisplayManager displayManager,
- @Main Handler handler) {
- mContext = context;
- mInflater = inflater;
- mFingerprintManager = checkNotNull(fingerprintManager);
- mWindowManager = windowManager;
- mFgExecutor = fgExecutor;
- mOrientationListener = new BiometricOrientationEventListener(
- context,
- () -> {
- onOrientationChanged();
- return Unit.INSTANCE;
- },
- displayManager,
- handler);
-
- mSensorProps = findFirstSidefps();
- checkArgument(mSensorProps != null);
-
- mCoreLayoutParams = new WindowManager.LayoutParams(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
- getCoreLayoutParamFlags(),
- PixelFormat.TRANSLUCENT);
- mCoreLayoutParams.setTitle(TAG);
- // Overrides default, avoiding status bars during layout
- mCoreLayoutParams.setFitInsetsTypes(0);
- mCoreLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
- mCoreLayoutParams.layoutInDisplayCutoutMode =
- WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-
- DisplayMetrics displayMetrics = new DisplayMetrics();
- windowManager.getDefaultDisplay().getMetrics(displayMetrics);
- mDisplayHeight = displayMetrics.heightPixels;
- mDisplayWidth = displayMetrics.widthPixels;
-
- mFingerprintManager.setSidefpsController(mSidefpsControllerImpl);
- }
-
- private void show() {
- mView = (SidefpsView) mInflater.inflate(R.layout.sidefps_view, null, false);
- mView.setSensorProperties(mSensorProps);
- mWindowManager.addView(mView, computeLayoutParams());
-
- mOrientationListener.enable();
- }
-
- private void hide() {
- if (mView != null) {
- mWindowManager.removeView(mView);
- mView.setOnTouchListener(null);
- mView.setOnHoverListener(null);
- mView = null;
- } else {
- Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
- }
-
- mOrientationListener.disable();
- }
-
- private void onOrientationChanged() {
- // If mView is null or if view is hidden, then return.
- if (mView == null || !mIsVisible) {
- return;
- }
-
- // If the overlay needs to be displayed with a new configuration, destroy the current
- // overlay, and re-create and show the overlay with the updated LayoutParams.
- hide();
- show();
- }
-
- @Nullable
- private FingerprintSensorPropertiesInternal findFirstSidefps() {
- for (FingerprintSensorPropertiesInternal props :
- mFingerprintManager.getSensorPropertiesInternal()) {
- if (props.isAnySidefpsType()) {
- // TODO(b/188690214): L155-L173 can be removed once sensorLocationX,
- // sensorLocationY, and sensorRadius are defined in sensorProps by the HAL
- int sensorLocationX = 25;
- int sensorLocationY = 610;
- int sensorRadius = 112;
-
- FingerprintSensorPropertiesInternal tempProps =
- new FingerprintSensorPropertiesInternal(
- props.sensorId,
- props.sensorStrength,
- props.maxEnrollmentsPerUser,
- props.componentInfo,
- props.sensorType,
- props.resetLockoutRequiresHardwareAuthToken,
- sensorLocationX,
- sensorLocationY,
- sensorRadius
- );
- props = tempProps;
- return props;
- }
- }
- return null;
- }
-
- private int getCoreLayoutParamFlags() {
- return WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- }
-
- /**
- * Computes layout params depending on orientation & folding configuration of device
- */
- private WindowManager.LayoutParams computeLayoutParams() {
- mCoreLayoutParams.flags = getCoreLayoutParamFlags();
- // Y value of top of affordance in portrait mode, X value of left of affordance in landscape
- int sfpsLocationY = mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
- int sfpsAffordanceHeight = mSensorProps.sensorRadius * 2;
-
- // Calculate coordinates of drawable area for the fps affordance, accounting for orientation
- switch (mContext.getDisplay().getRotation()) {
- case Surface.ROTATION_90:
- mCoreLayoutParams.x = sfpsLocationY;
- mCoreLayoutParams.y = 0;
- mCoreLayoutParams.height = SFPS_AFFORDANCE_WIDTH;
- mCoreLayoutParams.width = sfpsAffordanceHeight;
- break;
- case Surface.ROTATION_270:
- mCoreLayoutParams.x = mDisplayHeight - sfpsLocationY - sfpsAffordanceHeight;
- mCoreLayoutParams.y = mDisplayWidth - SFPS_AFFORDANCE_WIDTH;
- mCoreLayoutParams.height = SFPS_AFFORDANCE_WIDTH;
- mCoreLayoutParams.width = sfpsAffordanceHeight;
- break;
- default: // Portrait
- mCoreLayoutParams.x = mDisplayWidth - SFPS_AFFORDANCE_WIDTH;
- mCoreLayoutParams.y = sfpsLocationY;
- mCoreLayoutParams.height = sfpsAffordanceHeight;
- mCoreLayoutParams.width = SFPS_AFFORDANCE_WIDTH;
- }
- return mCoreLayoutParams;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
new file mode 100644
index 000000000000..41c7ebe6fa4b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.biometrics
+
+import android.content.Context
+import android.graphics.PixelFormat
+import android.graphics.Rect
+import android.hardware.biometrics.BiometricOverlayConstants
+import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
+import android.hardware.display.DisplayManager
+import android.hardware.fingerprint.FingerprintManager
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.hardware.fingerprint.ISidefpsController
+import android.os.Handler
+import android.util.Log
+import android.view.Display
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.Surface
+import android.view.View
+import android.view.WindowManager
+import androidx.annotation.RawRes
+import com.airbnb.lottie.LottieAnimationView
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.concurrency.DelayableExecutor
+import javax.inject.Inject
+
+private const val TAG = "SidefpsController"
+
+/**
+ * Shows and hides the side fingerprint sensor (side-fps) overlay and handles side fps touch events.
+ */
+@SysUISingleton
+class SidefpsController @Inject constructor(
+ private val context: Context,
+ private val layoutInflater: LayoutInflater,
+ fingerprintManager: FingerprintManager?,
+ private val windowManager: WindowManager,
+ @Main mainExecutor: DelayableExecutor,
+ displayManager: DisplayManager,
+ @Main handler: Handler
+) {
+ @VisibleForTesting
+ val sensorProps: FingerprintSensorPropertiesInternal = fingerprintManager
+ ?.sensorPropertiesInternal
+ ?.firstOrNull { it.isAnySidefpsType }
+ ?: throw IllegalStateException("no side fingerprint sensor")
+
+ @VisibleForTesting
+ val orientationListener = BiometricDisplayListener(
+ context,
+ displayManager,
+ handler,
+ BiometricDisplayListener.SensorType.SideFingerprint(sensorProps)
+ ) { onOrientationChanged() }
+
+ private var overlayView: View? = null
+ set(value) {
+ field?.let { oldView ->
+ windowManager.removeView(oldView)
+ orientationListener.disable()
+ }
+ field = value
+ field?.let { newView ->
+ windowManager.addView(newView, overlayViewParams)
+ orientationListener.enable()
+ }
+ }
+
+ private val overlayViewParams = WindowManager.LayoutParams(
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+ PixelFormat.TRANSLUCENT
+ ).apply {
+ title = TAG
+ fitInsetsTypes = 0 // overrides default, avoiding status bars during layout
+ gravity = Gravity.TOP or Gravity.LEFT
+ layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+ }
+
+ init {
+ fingerprintManager?.setSidefpsController(object : ISidefpsController.Stub() {
+ override fun show(
+ sensorId: Int,
+ @BiometricOverlayConstants.ShowReason reason: Int
+ ) = if (reason.isReasonToShow()) doShow() else hide(sensorId)
+
+ private fun doShow() = mainExecutor.execute {
+ if (overlayView == null) {
+ overlayView = createOverlayForDisplay()
+ } else {
+ Log.v(TAG, "overlay already shown")
+ }
+ }
+
+ override fun hide(sensorId: Int) = mainExecutor.execute { overlayView = null }
+ })
+ }
+
+ private fun onOrientationChanged() {
+ if (overlayView != null) {
+ overlayView = createOverlayForDisplay()
+ }
+ }
+
+ private fun createOverlayForDisplay(): View {
+ val view = layoutInflater.inflate(R.layout.sidefps_view, null, false)
+ val display = context.display!!
+
+ val lottie = view.findViewById(R.id.sidefps_animation) as LottieAnimationView
+ lottie.setAnimation(display.asSideFpsAnimation())
+ view.rotation = display.asSideFpsAnimationRotation()
+
+ updateOverlayParams(display, lottie.composition?.bounds ?: Rect())
+ lottie.addLottieOnCompositionLoadedListener {
+ if (overlayView == view) {
+ updateOverlayParams(display, it.bounds)
+ windowManager.updateViewLayout(overlayView, overlayViewParams)
+ }
+ }
+
+ return view
+ }
+
+ private fun updateOverlayParams(display: Display, bounds: Rect) {
+ val isPortrait = display.isPortrait()
+ val size = windowManager.maximumWindowMetrics.bounds
+ val displayWidth = if (isPortrait) size.width() else size.height()
+ val displayHeight = if (isPortrait) size.height() else size.width()
+ val offsets = sensorProps.getLocation(display.uniqueId).let { location ->
+ if (location == null) {
+ Log.w(TAG, "No location specified for display: ${display.uniqueId}")
+ }
+ location ?: sensorProps.location
+ }
+
+ // ignore sensorLocationX and sensorRadius since it's assumed to be on the side
+ // of the device and centered at sensorLocationY
+ val (x, y) = when (display.rotation) {
+ Surface.ROTATION_90 ->
+ Pair(offsets.sensorLocationY, 0)
+ Surface.ROTATION_270 ->
+ Pair(displayHeight - offsets.sensorLocationY - bounds.width(), displayWidth)
+ Surface.ROTATION_180 ->
+ Pair(0, displayHeight - offsets.sensorLocationY - bounds.height())
+ else ->
+ Pair(displayWidth, offsets.sensorLocationY)
+ }
+ overlayViewParams.x = x
+ overlayViewParams.y = y
+ }
+}
+
+@BiometricOverlayConstants.ShowReason
+private fun Int.isReasonToShow(): Boolean = when (this) {
+ REASON_AUTH_KEYGUARD -> false
+ else -> true
+}
+
+@RawRes
+private fun Display.asSideFpsAnimation(): Int = when (rotation) {
+ Surface.ROTATION_0 -> R.raw.sfps_pulse
+ Surface.ROTATION_180 -> R.raw.sfps_pulse
+ else -> R.raw.sfps_pulse_landscape
+}
+
+private fun Display.asSideFpsAnimationRotation(): Float = when (rotation) {
+ Surface.ROTATION_180 -> 180f
+ Surface.ROTATION_270 -> 180f
+ else -> 0f
+}
+
+private fun Display.isPortrait(): Boolean =
+ rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java
deleted file mode 100644
index 4fc59d60ff62..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsView.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static com.android.systemui.biometrics.SidefpsController.SFPS_AFFORDANCE_WIDTH;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.util.AttributeSet;
-import android.view.Surface;
-import android.widget.FrameLayout;
-
-/**
- * A view containing a normal drawable view for sidefps events.
- */
-public class SidefpsView extends FrameLayout {
- private static final String TAG = "SidefpsView";
- private static final int POINTER_SIZE_PX = 50;
- private static final int ROUND_RADIUS = 15;
-
- @NonNull private final RectF mSensorRect;
- @NonNull private final Paint mSensorRectPaint;
- @NonNull private final Paint mPointerText;
- @NonNull private final Context mContext;
-
- // Used to obtain the sensor location.
- @NonNull private FingerprintSensorPropertiesInternal mSensorProps;
- @Surface.Rotation private int mOrientation;
-
- public SidefpsView(Context context, AttributeSet attrs) {
- super(context, attrs);
- super.setWillNotDraw(false);
- mContext = context;
- mPointerText = new Paint(0 /* flags */);
- mPointerText.setAntiAlias(true);
- mPointerText.setColor(Color.WHITE);
- mPointerText.setTextSize(POINTER_SIZE_PX);
-
- mSensorRect = new RectF();
- mSensorRectPaint = new Paint(0 /* flags */);
- mSensorRectPaint.setAntiAlias(true);
- mSensorRectPaint.setColor(Color.BLUE); // TODO: Fix Color
- mSensorRectPaint.setStyle(Paint.Style.FILL);
- }
-
- void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) {
- mSensorProps = properties;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawRoundRect(mSensorRect, ROUND_RADIUS, ROUND_RADIUS, mSensorRectPaint);
- int x, y;
- if (mOrientation == Surface.ROTATION_90 || mOrientation == Surface.ROTATION_270) {
- x = mSensorProps.sensorRadius + 10;
- y = SFPS_AFFORDANCE_WIDTH / 2 + 15;
- } else {
- x = SFPS_AFFORDANCE_WIDTH / 2 - 10;
- y = mSensorProps.sensorRadius + 30;
- }
- canvas.drawText(
- ">",
- x,
- y,
- mPointerText
- );
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- mOrientation = mContext.getDisplay().getRotation();
- if (mOrientation == Surface.ROTATION_90 || mOrientation == Surface.ROTATION_270) {
- right = mSensorProps.sensorRadius * 2;
- bottom = SFPS_AFFORDANCE_WIDTH;
- } else {
- right = SFPS_AFFORDANCE_WIDTH;
- bottom = mSensorProps.sensorRadius * 2;
- }
-
- mSensorRect.set(
- 0,
- 0,
- right,
- bottom);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 62b9fd44d800..56418680e74d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -16,7 +16,7 @@
package com.android.systemui.biometrics;
-import static android.hardware.fingerprint.IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD;
+import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -32,6 +32,8 @@ import android.content.IntentFilter;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.RectF;
+import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -39,7 +41,6 @@ import android.hardware.fingerprint.IUdfpsOverlayController;
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.media.AudioAttributes;
import android.os.Handler;
-import android.os.Looper;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -63,7 +64,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -71,6 +71,7 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -93,7 +94,7 @@ import kotlin.Unit;
* controls/manages all UDFPS sensors. In other words, a single controller is registered with
* {@link com.android.server.biometrics.sensors.fingerprint.FingerprintService}, and interfaces such
* as {@link FingerprintManager#onPointerDown(int, int, int, float, float)} or
- * {@link IUdfpsOverlayController#showUdfpsOverlay(int)}should all have
+ * {@link IUdfpsOverlayController#showUdfpsOverlay(int)} should all have
* {@code sensorId} parameters.
*/
@SuppressWarnings("deprecation")
@@ -117,9 +118,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
@NonNull private final DumpManager mDumpManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @NonNull private final KeyguardViewMediator mKeyguardViewMediator;
@Nullable private final Vibrator mVibrator;
- @NonNull private final Handler mMainHandler;
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
@@ -128,7 +127,9 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final KeyguardBypassController mKeyguardBypassController;
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final SystemClock mSystemClock;
- @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;
+ @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
+ @NonNull private final UnlockedScreenOffAnimationController
+ mUnlockedScreenOffAnimationController;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -241,8 +242,8 @@ public class UdfpsController implements DozeReceiver {
@NonNull IUdfpsOverlayControllerCallback callback) {
mFgExecutor.execute(() -> {
final UdfpsEnrollHelper enrollHelper;
- if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR
- || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) {
+ if (reason == BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
+ || reason == BiometricOverlayConstants.REASON_ENROLL_ENROLLING) {
enrollHelper = new UdfpsEnrollHelper(mContext, reason);
} else {
enrollHelper = null;
@@ -320,7 +321,7 @@ public class UdfpsController implements DozeReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (mServerRequest != null
- && mServerRequest.mRequestReason != REASON_AUTH_FPM_KEYGUARD
+ && mServerRequest.mRequestReason != REASON_AUTH_KEYGUARD
&& Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
Log.d(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received, mRequestReason: "
+ mServerRequest.mRequestReason);
@@ -517,7 +518,6 @@ public class UdfpsController implements DozeReceiver {
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull DumpManager dumpManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
- @NonNull KeyguardViewMediator keyguardViewMediator,
@NonNull FalsingManager falsingManager,
@NonNull PowerManager powerManager,
@NonNull AccessibilityManager accessibilityManager,
@@ -531,11 +531,11 @@ public class UdfpsController implements DozeReceiver {
@NonNull DisplayManager displayManager,
@Main Handler mainHandler,
@NonNull ConfigurationController configurationController,
- @NonNull SystemClock systemClock) {
+ @NonNull SystemClock systemClock,
+ @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
mContext = context;
mExecution = execution;
// TODO (b/185124905): inject main handler and vibrator once done prototyping
- mMainHandler = new Handler(Looper.getMainLooper());
mVibrator = vibrator;
mInflater = inflater;
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
@@ -549,7 +549,6 @@ public class UdfpsController implements DozeReceiver {
mKeyguardViewManager = statusBarKeyguardViewManager;
mDumpManager = dumpManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mKeyguardViewMediator = keyguardViewMediator;
mFalsingManager = falsingManager;
mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager;
@@ -557,21 +556,23 @@ public class UdfpsController implements DozeReceiver {
mHbmProvider = hbmProvider.orElse(null);
screenLifecycle.addObserver(mScreenObserver);
mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
- mOrientationListener = new BiometricOrientationEventListener(
- context,
- () -> {
- onOrientationChanged();
- return Unit.INSTANCE;
- },
- displayManager,
- mainHandler);
mKeyguardBypassController = keyguardBypassController;
mConfigurationController = configurationController;
mSystemClock = systemClock;
+ mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mSensorProps = findFirstUdfps();
// At least one UDFPS sensor exists
checkArgument(mSensorProps != null);
+ mOrientationListener = new BiometricDisplayListener(
+ context,
+ displayManager,
+ mainHandler,
+ new BiometricDisplayListener.SensorType.UnderDisplayFingerprint(mSensorProps),
+ () -> {
+ onOrientationChanged();
+ return Unit.INSTANCE;
+ });
mCoreLayoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
@@ -641,10 +642,11 @@ public class UdfpsController implements DozeReceiver {
// on lockscreen and for the udfps light reveal animation on keyguard.
// Keyguard is only shown in portrait mode for now, so this will need to
// be updated if that ever changes.
- return new RectF(mSensorProps.sensorLocationX - mSensorProps.sensorRadius,
- mSensorProps.sensorLocationY - mSensorProps.sensorRadius,
- mSensorProps.sensorLocationX + mSensorProps.sensorRadius,
- mSensorProps.sensorLocationY + mSensorProps.sensorRadius);
+ final SensorLocationInternal location = mSensorProps.getLocation();
+ return new RectF(location.sensorLocationX - location.sensorRadius,
+ location.sensorLocationY - location.sensorRadius,
+ location.sensorLocationX + location.sensorRadius,
+ location.sensorLocationY + location.sensorRadius);
}
private void updateOverlay() {
@@ -657,6 +659,20 @@ public class UdfpsController implements DozeReceiver {
}
}
+ private boolean shouldRotate(@Nullable UdfpsAnimationViewController animation) {
+ if (!(animation instanceof UdfpsKeyguardViewController)) {
+ // always rotate view if we're not on the keyguard
+ return true;
+ }
+
+ // on the keyguard, make sure we don't rotate if we're going to sleep or not occluded
+ if (mKeyguardUpdateMonitor.isGoingToSleep() || !mKeyguardStateController.isOccluded()) {
+ return false;
+ }
+
+ return true;
+ }
+
private WindowManager.LayoutParams computeLayoutParams(
@Nullable UdfpsAnimationViewController animation) {
final int paddingX = animation != null ? animation.getPaddingX() : 0;
@@ -668,10 +684,11 @@ public class UdfpsController implements DozeReceiver {
}
// Default dimensions assume portrait mode.
- mCoreLayoutParams.x = mSensorProps.sensorLocationX - mSensorProps.sensorRadius - paddingX;
- mCoreLayoutParams.y = mSensorProps.sensorLocationY - mSensorProps.sensorRadius - paddingY;
- mCoreLayoutParams.height = 2 * mSensorProps.sensorRadius + 2 * paddingX;
- mCoreLayoutParams.width = 2 * mSensorProps.sensorRadius + 2 * paddingY;
+ final SensorLocationInternal location = mSensorProps.getLocation();
+ mCoreLayoutParams.x = location.sensorLocationX - location.sensorRadius - paddingX;
+ mCoreLayoutParams.y = location.sensorLocationY - location.sensorRadius - paddingY;
+ mCoreLayoutParams.height = 2 * location.sensorRadius + 2 * paddingX;
+ mCoreLayoutParams.width = 2 * location.sensorRadius + 2 * paddingY;
Point p = new Point();
// Gets the size based on the current rotation of the display.
@@ -680,24 +697,28 @@ public class UdfpsController implements DozeReceiver {
// Transform dimensions if the device is in landscape mode
switch (mContext.getDisplay().getRotation()) {
case Surface.ROTATION_90:
- if (animation instanceof UdfpsKeyguardViewController
- && mKeyguardUpdateMonitor.isGoingToSleep()) {
+ if (!shouldRotate(animation)) {
+ Log.v(TAG, "skip rotating udfps location ROTATION_90");
break;
+ } else {
+ Log.v(TAG, "rotate udfps location ROTATION_90");
}
- mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+ mCoreLayoutParams.x = location.sensorLocationY - location.sensorRadius
- paddingX;
- mCoreLayoutParams.y = p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+ mCoreLayoutParams.y = p.y - location.sensorLocationX - location.sensorRadius
- paddingY;
break;
case Surface.ROTATION_270:
- if (animation instanceof UdfpsKeyguardViewController
- && mKeyguardUpdateMonitor.isGoingToSleep()) {
+ if (!shouldRotate(animation)) {
+ Log.v(TAG, "skip rotating udfps location ROTATION_270");
break;
+ } else {
+ Log.v(TAG, "rotate udfps location ROTATION_270");
}
- mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+ mCoreLayoutParams.x = p.x - location.sensorLocationY - location.sensorRadius
- paddingX;
- mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+ mCoreLayoutParams.y = location.sensorLocationX - location.sensorRadius
- paddingY;
break;
@@ -747,9 +768,9 @@ public class UdfpsController implements DozeReceiver {
// This view overlaps the sensor area, so prevent it from being selectable
// during a11y.
- if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR
- || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING
- || reason == IUdfpsOverlayController.REASON_AUTH_BP) {
+ if (reason == BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
+ || reason == BiometricOverlayConstants.REASON_ENROLL_ENROLLING
+ || reason == BiometricOverlayConstants.REASON_AUTH_BP) {
mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
}
@@ -767,8 +788,8 @@ public class UdfpsController implements DozeReceiver {
private UdfpsAnimationViewController inflateUdfpsAnimation(int reason) {
switch (reason) {
- case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR:
- case IUdfpsOverlayController.REASON_ENROLL_ENROLLING:
+ case BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR:
+ case BiometricOverlayConstants.REASON_ENROLL_ENROLLING:
UdfpsEnrollView enrollView = (UdfpsEnrollView) mInflater.inflate(
R.layout.udfps_enroll_view, null);
mView.addView(enrollView);
@@ -780,7 +801,7 @@ public class UdfpsController implements DozeReceiver {
mStatusBarOptional,
mDumpManager
);
- case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
+ case BiometricOverlayConstants.REASON_AUTH_KEYGUARD:
UdfpsKeyguardView keyguardView = (UdfpsKeyguardView)
mInflater.inflate(R.layout.udfps_keyguard_view, null);
mView.addView(keyguardView);
@@ -790,16 +811,15 @@ public class UdfpsController implements DozeReceiver {
mStatusBarOptional,
mKeyguardViewManager,
mKeyguardUpdateMonitor,
- mFgExecutor,
mDumpManager,
- mKeyguardViewMediator,
mLockscreenShadeTransitionController,
mConfigurationController,
mSystemClock,
mKeyguardStateController,
+ mUnlockedScreenOffAnimationController,
this
);
- case IUdfpsOverlayController.REASON_AUTH_BP:
+ case BiometricOverlayConstants.REASON_AUTH_BP:
// note: empty controller, currently shows no visual affordance
UdfpsBpView bpView = (UdfpsBpView) mInflater.inflate(R.layout.udfps_bp_view, null);
mView.addView(bpView);
@@ -809,7 +829,7 @@ public class UdfpsController implements DozeReceiver {
mStatusBarOptional,
mDumpManager
);
- case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER:
+ case BiometricOverlayConstants.REASON_AUTH_OTHER:
UdfpsFpmOtherView authOtherView = (UdfpsFpmOtherView)
mInflater.inflate(R.layout.udfps_fpm_other_view, null);
mView.addView(authOtherView);
@@ -861,6 +881,10 @@ public class UdfpsController implements DozeReceiver {
return;
}
+ if (!mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
+ return;
+ }
+
mAodInterruptRunnable = () -> {
mIsAodInterruptActive = true;
// Since the sensor that triggers the AOD interrupt doesn't provide
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
index 7ccfb865cd5a..6cc8acf07c50 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.util.Log;
import android.view.Surface;
@@ -93,7 +94,7 @@ public class UdfpsDialogMeasureAdapter {
// Go through each of the children and do the custom measurement.
int totalHeight = 0;
final int numChildren = mView.getChildCount();
- final int sensorDiameter = mSensorProps.sensorRadius * 2;
+ final int sensorDiameter = mSensorProps.getLocation().sensorRadius * 2;
for (int i = 0; i < numChildren; i++) {
final View child = mView.getChildAt(i);
if (child.getId() == R.id.biometric_icon_frame) {
@@ -160,7 +161,7 @@ public class UdfpsDialogMeasureAdapter {
final int horizontalSpacerWidth = calculateHorizontalSpacerWidthForLandscape(
mSensorProps, displayWidth, dialogMargin, horizontalInset);
- final int sensorDiameter = mSensorProps.sensorRadius * 2;
+ final int sensorDiameter = mSensorProps.getLocation().sensorRadius * 2;
final int remeasuredWidth = sensorDiameter + 2 * horizontalSpacerWidth;
int remeasuredHeight = 0;
@@ -257,10 +258,10 @@ public class UdfpsDialogMeasureAdapter {
@NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayHeightPx,
int textIndicatorHeightPx, int buttonBarHeightPx, int dialogMarginPx,
int navbarBottomInsetPx) {
-
+ final SensorLocationInternal location = sensorProperties.getLocation();
final int sensorDistanceFromBottom = displayHeightPx
- - sensorProperties.sensorLocationY
- - sensorProperties.sensorRadius;
+ - location.sensorLocationY
+ - location.sensorRadius;
final int spacerHeight = sensorDistanceFromBottom
- textIndicatorHeightPx
@@ -318,10 +319,10 @@ public class UdfpsDialogMeasureAdapter {
static int calculateHorizontalSpacerWidthForLandscape(
@NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayWidthPx,
int dialogMarginPx, int navbarHorizontalInsetPx) {
-
+ final SensorLocationInternal location = sensorProperties.getLocation();
final int sensorDistanceFromEdge = displayWidthPx
- - sensorProperties.sensorLocationY
- - sensorProperties.sensorRadius;
+ - location.sensorLocationY
+ - location.sensorRadius;
final int horizontalPadding = sensorDistanceFromEdge
- dialogMarginPx
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index 19148e383005..c6d2192b5b05 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -20,7 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.PointF;
-import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.hardware.biometrics.BiometricOverlayConstants;
import android.os.Build;
import android.os.UserHandle;
import android.provider.Settings;
@@ -119,7 +119,7 @@ public class UdfpsEnrollHelper {
}
boolean shouldShowProgressBar() {
- return mEnrollReason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING;
+ return mEnrollReason == BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
}
void onEnrollmentProgress(int remaining) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
index d9edef48b3d4..c83006d8092a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -63,7 +63,7 @@ public class UdfpsEnrollView extends UdfpsAnimationView {
void updateSensorLocation(@NonNull FingerprintSensorPropertiesInternal sensorProps) {
View fingerprintAccessibilityView = findViewById(R.id.udfps_enroll_accessibility_view);
- final int sensorHeight = sensorProps.sensorRadius * 2;
+ final int sensorHeight = sensorProps.getLocation().sensorRadius * 2;
final int sensorWidth = sensorHeight;
ViewGroup.LayoutParams params = fingerprintAccessibilityView.getLayoutParams();
params.width = sensorWidth;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 8ce4c3b8577d..db93b26d99e5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -26,36 +26,34 @@ import android.view.MotionEvent;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Optional;
-
/**
* Class that coordinates non-HBM animations during keyguard authentication.
*/
public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> {
@NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @NonNull private final DelayableExecutor mExecutor;
- @NonNull private final KeyguardViewMediator mKeyguardViewMediator;
@NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final SystemClock mSystemClock;
@NonNull private final KeyguardStateController mKeyguardStateController;
@NonNull private final UdfpsController mUdfpsController;
+ @NonNull private final UnlockedScreenOffAnimationController
+ mUnlockedScreenOffAnimationController;
private boolean mShowingUdfpsBouncer;
private boolean mUdfpsRequested;
@@ -82,24 +80,22 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
@NonNull Optional<StatusBar> statusBarOptional,
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
- @NonNull DelayableExecutor mainDelayableExecutor,
@NonNull DumpManager dumpManager,
- @NonNull KeyguardViewMediator keyguardViewMediator,
@NonNull LockscreenShadeTransitionController transitionController,
@NonNull ConfigurationController configurationController,
@NonNull SystemClock systemClock,
@NonNull KeyguardStateController keyguardStateController,
+ @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
@NonNull UdfpsController udfpsController) {
super(view, statusBarStateController, statusBarOptional, dumpManager);
mKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mExecutor = mainDelayableExecutor;
- mKeyguardViewMediator = keyguardViewMediator;
mLockScreenShadeTransitionController = transitionController;
mConfigurationController = configurationController;
mSystemClock = systemClock;
mKeyguardStateController = keyguardStateController;
mUdfpsController = udfpsController;
+ mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
}
@Override
@@ -138,6 +134,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this);
+ mUnlockedScreenOffAnimationController.addCallback(mUnlockedScreenOffCallback);
}
@Override
@@ -156,6 +153,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
}
+ mUnlockedScreenOffAnimationController.removeCallback(mUnlockedScreenOffCallback);
}
@Override
@@ -428,4 +426,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud
updatePauseAuth();
}
};
+
+ private final UnlockedScreenOffAnimationController.Callback mUnlockedScreenOffCallback =
+ (linear, eased) -> mStateListener.onDozeAmountChanged(linear, eased);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 15f77ffc08fd..6d31ef0e7701 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -25,6 +25,7 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Build;
import android.os.UserHandle;
@@ -141,11 +142,12 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin
: mAnimationViewController.getPaddingX();
int paddingY = mAnimationViewController == null ? 0
: mAnimationViewController.getPaddingY();
+ final SensorLocationInternal location = mSensorProps.getLocation();
mSensorRect.set(
paddingX,
paddingY,
- 2 * mSensorProps.sensorRadius + paddingX,
- 2 * mSensorProps.sensorRadius + paddingY);
+ 2 * location.sensorRadius + paddingX,
+ 2 * location.sensorRadius + paddingY);
if (mAnimationViewController != null) {
mAnimationViewController.onSensorRectUpdated(new RectF(mSensorRect));
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index 4104e3184efd..46a03e809b06 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -16,6 +16,10 @@
package com.android.systemui.controls.ui
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
@@ -23,18 +27,25 @@ import android.view.WindowInsets
import android.view.WindowInsets.Type
import com.android.systemui.R
+import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.management.ControlsAnimations
import com.android.systemui.util.LifecycleActivity
import javax.inject.Inject
/**
- * Displays Device Controls inside an activity
+ * Displays Device Controls inside an activity. This activity is available to be displayed over the
+ * lockscreen if the user has allowed it via
+ * [android.provider.Settings.Secure.LOCKSCREEN_SHOW_CONTROLS]. This activity will be
+ * destroyed on SCREEN_OFF events, due to issues with occluded activities over lockscreen as well as
+ * user expectations for the activity to not continue running.
*/
class ControlsActivity @Inject constructor(
- private val uiController: ControlsUiController
+ private val uiController: ControlsUiController,
+ private val broadcastDispatcher: BroadcastDispatcher
) : LifecycleActivity() {
private lateinit var parent: ViewGroup
+ private lateinit var broadcastReceiver: BroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -62,6 +73,8 @@ class ControlsActivity @Inject constructor(
WindowInsets.CONSUMED
}
}
+
+ initBroadcastReceiver()
}
override fun onResume() {
@@ -83,4 +96,25 @@ class ControlsActivity @Inject constructor(
uiController.hide()
}
+
+ override fun onDestroy() {
+ super.onDestroy()
+
+ broadcastDispatcher.unregisterReceiver(broadcastReceiver)
+ }
+
+ private fun initBroadcastReceiver() {
+ broadcastReceiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val action = intent.getAction()
+ if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ finish()
+ }
+ }
+ }
+
+ val filter = IntentFilter()
+ filter.addAction(Intent.ACTION_SCREEN_OFF)
+ broadcastDispatcher.registerReceiver(broadcastReceiver, filter)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 567d0cb3e6cb..72da7f4f893a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -256,13 +256,13 @@ class ControlsUiControllerImpl @Inject constructor (
// Force animations when transitioning from a dialog to an activity
intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
- if (keyguardStateController.isUnlocked()) {
+ if (keyguardStateController.isShowing()) {
+ activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */)
+ } else {
activityContext.startActivity(
intent,
ActivityOptions.makeSceneTransitionAnimation(activityContext as Activity).toBundle()
)
- } else {
- activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 41964652ac49..845d7dc26ee5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -214,6 +214,14 @@ public class DozeLog implements Dumpable {
}
/**
+ * Appends display state delayed by UDFPS event to the logs
+ * @param delayedDisplayState the display screen state that was delayed
+ */
+ public void traceDisplayStateDelayedByUdfps(int delayedDisplayState) {
+ mLogger.logDisplayStateDelayedByUdfps(delayedDisplayState);
+ }
+
+ /**
* Appends display state changed event to the logs
* @param displayState new DozeMachine state
*/
@@ -267,6 +275,13 @@ public class DozeLog implements Dumpable {
}
/**
+ * Appends sensor event dropped event to logs
+ */
+ public void traceSensorEventDropped(int sensorEvent, String reason) {
+ mLogger.logSensorEventDropped(sensorEvent, reason);
+ }
+
+ /**
* Appends pulse dropped event to logs
* @param reason why the pulse was dropped
*/
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 9bc74be9b9c3..dc186182432f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -160,6 +160,14 @@ class DozeLogger @Inject constructor(
})
}
+ fun logDisplayStateDelayedByUdfps(delayedDisplayState: Int) {
+ buffer.log(TAG, INFO, {
+ str1 = Display.stateToString(delayedDisplayState)
+ }, {
+ "Delaying display state change to: $str1 due to UDFPS activity"
+ })
+ }
+
fun logDisplayStateChanged(displayState: Int) {
buffer.log(TAG, INFO, {
str1 = Display.stateToString(displayState)
@@ -197,6 +205,15 @@ class DozeLogger @Inject constructor(
})
}
+ fun logSensorEventDropped(sensorEvent: Int, reason: String) {
+ buffer.log(TAG, INFO, {
+ int1 = sensorEvent
+ str1 = reason
+ }, {
+ "SensorEvent [$int1] dropped, reason=$str1"
+ })
+ }
+
fun logPulseDropped(reason: String) {
buffer.log(TAG, INFO, {
str1 = reason
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 98d2739836a9..da7b389fbd36 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -29,7 +29,6 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
-import android.view.Display;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.dagger.BrightnessSensor;
@@ -111,7 +110,15 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case INITIALIZED:
+ resetBrightnessToDefault();
+ break;
+ case DOZE_AOD:
+ case DOZE_REQUEST_PULSE:
+ case DOZE_AOD_DOCKED:
+ setLightSensorEnabled(true);
+ break;
case DOZE:
+ setLightSensorEnabled(false);
resetBrightnessToDefault();
break;
case FINISH:
@@ -124,22 +131,6 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi
}
}
- @Override
- public void onScreenState(int state) {
- boolean isDockedScreenOn = state == Display.STATE_ON && mDockManager.isDocked();
- if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND
- || (isDockedScreenOn && shouldRegisterLightSensorWhenScreenOnDocked())) {
- setLightSensorEnabled(true);
- } else {
- setLightSensorEnabled(false);
- }
- }
-
- private boolean shouldRegisterLightSensorWhenScreenOnDocked() {
- return !mDozeParameters.brightnessUsesProx()
- || !mDozeParameters.getSelectivelyRegisterSensorsUsingProx();
- }
-
private void onDestroy() {
setLightSensorEnabled(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 8c50a16b566f..8f1486b0c7cb 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -26,6 +26,11 @@ import android.os.Handler;
import android.util.Log;
import android.view.Display;
+import androidx.annotation.Nullable;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.doze.dagger.WrappedService;
@@ -34,6 +39,7 @@ import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
import javax.inject.Inject;
+import javax.inject.Provider;
/**
* Controls the screen when dozing.
@@ -56,23 +62,61 @@ public class DozeScreenState implements DozeMachine.Part {
*/
public static final int ENTER_DOZE_HIDE_WALLPAPER_DELAY = 2500;
+ /**
+ * Add an extra delay to the transition to DOZE when udfps is current activated before
+ * the display state transitions from ON => DOZE.
+ */
+ public static final int UDFPS_DISPLAY_STATE_DELAY = 1200;
+
private final DozeMachine.Service mDozeService;
private final Handler mHandler;
private final Runnable mApplyPendingScreenState = this::applyPendingScreenState;
private final DozeParameters mParameters;
private final DozeHost mDozeHost;
+ private final AuthController mAuthController;
+ private final Provider<UdfpsController> mUdfpsControllerProvider;
+ @Nullable private UdfpsController mUdfpsController;
+ private final DozeLog mDozeLog;
private int mPendingScreenState = Display.STATE_UNKNOWN;
private SettableWakeLock mWakeLock;
@Inject
- public DozeScreenState(@WrappedService DozeMachine.Service service, @Main Handler handler,
- DozeHost host, DozeParameters parameters, WakeLock wakeLock) {
+ public DozeScreenState(
+ @WrappedService DozeMachine.Service service,
+ @Main Handler handler,
+ DozeHost host,
+ DozeParameters parameters,
+ WakeLock wakeLock,
+ AuthController authController,
+ Provider<UdfpsController> udfpsControllerProvider,
+ DozeLog dozeLog) {
mDozeService = service;
mHandler = handler;
mParameters = parameters;
mDozeHost = host;
mWakeLock = new SettableWakeLock(wakeLock, TAG);
+ mAuthController = authController;
+ mUdfpsControllerProvider = udfpsControllerProvider;
+ mDozeLog = dozeLog;
+
+ updateUdfpsController();
+ if (mUdfpsController == null) {
+ mAuthController.addCallback(new AuthController.Callback() {
+ @Override
+ public void onAllAuthenticatorsRegistered() {
+ updateUdfpsController();
+ }
+ });
+ }
+ }
+
+ private void updateUdfpsController() {
+ if (mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) {
+ mUdfpsController = mUdfpsControllerProvider.get();
+ } else {
+ mUdfpsController = null;
+ }
}
@Override
@@ -110,21 +154,28 @@ public class DozeScreenState implements DozeMachine.Part {
mPendingScreenState = screenState;
// Delay screen state transitions even longer while animations are running.
- boolean shouldDelayTransition = newState == DOZE_AOD
+ boolean shouldDelayTransitionEnteringDoze = newState == DOZE_AOD
&& mParameters.shouldControlScreenOff() && !turningOn;
- if (shouldDelayTransition) {
+ // Delay screen state transition longer if UDFPS is actively authenticating a fp
+ boolean shouldDelayTransitionForUDFPS = newState == DOZE_AOD
+ && mUdfpsController != null && mUdfpsController.isFingerDown();
+
+ if (shouldDelayTransitionEnteringDoze || shouldDelayTransitionForUDFPS) {
mWakeLock.setAcquired(true);
}
if (!messagePending) {
if (DEBUG) {
Log.d(TAG, "Display state changed to " + screenState + " delayed by "
- + (shouldDelayTransition ? ENTER_DOZE_DELAY : 1));
+ + (shouldDelayTransitionEnteringDoze ? ENTER_DOZE_DELAY : 1));
}
- if (shouldDelayTransition) {
+ if (shouldDelayTransitionEnteringDoze) {
mHandler.postDelayed(mApplyPendingScreenState, ENTER_DOZE_DELAY);
+ } else if (shouldDelayTransitionForUDFPS) {
+ mDozeLog.traceDisplayStateDelayedByUdfps(mPendingScreenState);
+ mHandler.postDelayed(mApplyPendingScreenState, UDFPS_DISPLAY_STATE_DELAY);
} else {
mHandler.post(mApplyPendingScreenState);
}
@@ -139,6 +190,12 @@ public class DozeScreenState implements DozeMachine.Part {
}
private void applyPendingScreenState() {
+ if (mUdfpsController != null && mUdfpsController.isFingerDown()) {
+ mDozeLog.traceDisplayStateDelayedByUdfps(mPendingScreenState);
+ mHandler.postDelayed(mApplyPendingScreenState, UDFPS_DISPLAY_STATE_DELAY);
+ return;
+ }
+
applyScreenState(mPendingScreenState);
mPendingScreenState = Display.STATE_UNKNOWN;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 455f3c0d6ac2..756adca724e9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -41,6 +41,7 @@ import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeMachine.State;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.Assert;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.sensors.AsyncSensorManager;
@@ -92,6 +93,7 @@ public class DozeTriggers implements DozeMachine.Part {
private final BroadcastDispatcher mBroadcastDispatcher;
private final AuthController mAuthController;
private final DelayableExecutor mMainExecutor;
+ private final KeyguardStateController mKeyguardStateController;
private final UiEventLogger mUiEventLogger;
private long mNotificationPulseTime;
@@ -179,7 +181,8 @@ public class DozeTriggers implements DozeMachine.Part {
DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher,
SecureSettings secureSettings, AuthController authController,
@Main DelayableExecutor mainExecutor,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ KeyguardStateController keyguardStateController) {
mContext = context;
mDozeHost = dozeHost;
mConfig = config;
@@ -198,6 +201,7 @@ public class DozeTriggers implements DozeMachine.Part {
mAuthController = authController;
mMainExecutor = mainExecutor;
mUiEventLogger = uiEventLogger;
+ mKeyguardStateController = keyguardStateController;
}
@Override
@@ -294,6 +298,7 @@ public class DozeTriggers implements DozeMachine.Part {
proximityCheckThenCall((result) -> {
if (result != null && result) {
// In pocket, drop event.
+ mDozeLog.traceSensorEventDropped(pulseReason, "prox reporting near");
return;
}
if (isDoubleTap || isTap) {
@@ -302,6 +307,10 @@ public class DozeTriggers implements DozeMachine.Part {
}
gentleWakeUp(pulseReason);
} else if (isPickup) {
+ if (shouldDropPickupEvent()) {
+ mDozeLog.traceSensorEventDropped(pulseReason, "keyguard occluded");
+ return;
+ }
gentleWakeUp(pulseReason);
} else if (isUdfpsLongPress) {
final State state = mMachine.getState();
@@ -320,7 +329,7 @@ public class DozeTriggers implements DozeMachine.Part {
}, true /* alreadyPerformedProxCheck */, pulseReason);
}
- if (isPickup) {
+ if (isPickup && !shouldDropPickupEvent()) {
final long timeSinceNotification =
SystemClock.elapsedRealtime() - mNotificationPulseTime;
final boolean withinVibrationThreshold =
@@ -329,6 +338,10 @@ public class DozeTriggers implements DozeMachine.Part {
}
}
+ private boolean shouldDropPickupEvent() {
+ return mKeyguardStateController.isOccluded();
+ }
+
private void gentleWakeUp(int reason) {
// Log screen wake up reason (lift/pickup, tap, double-tap)
Optional.ofNullable(DozingUpdateUiEvent.fromReason(reason))
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 281771860cb7..1561eb6368a8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -818,6 +818,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
private final KeyguardStateController mKeyguardStateController;
private final Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
+ private boolean mWallpaperSupportsAmbientMode;
/**
* Injected constructor. See {@link KeyguardModule}.
@@ -2089,13 +2090,14 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
int flags = 0;
if (mKeyguardViewControllerLazy.get().shouldDisableWindowAnimationsForUnlock()
- || (mWakeAndUnlocking && !mPulsing)
- || isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()) {
+ || mWakeAndUnlocking && !mWallpaperSupportsAmbientMode) {
flags |= WindowManagerPolicyConstants
.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
}
if (mKeyguardViewControllerLazy.get().isGoingToNotificationShade()
- || (mWakeAndUnlocking && mPulsing)) {
+ || mWakeAndUnlocking && mWallpaperSupportsAmbientMode) {
+ // When the wallpaper supports ambient mode, the scrim isn't fully opaque during
+ // wake and unlock and we should fade in the app on top of the wallpaper
flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
}
if (mKeyguardViewControllerLazy.get().isUnlockWithWallpaper()) {
@@ -2565,9 +2567,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
private void handleNotifyScreenTurnedOn() {
Trace.beginSection("KeyguardViewMediator#handleNotifyScreenTurnedOn");
- if (LatencyTracker.isEnabled(mContext)) {
- LatencyTracker.getInstance(mContext).onActionEnd(LatencyTracker.ACTION_TURN_ON_SCREEN);
- }
synchronized (this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOn");
mKeyguardViewControllerLazy.get().onScreenTurnedOn();
@@ -2784,6 +2783,15 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable,
mPulsing = pulsing;
}
+ /**
+ * Set if the wallpaper supports ambient mode. This is used to trigger the right animation.
+ * In case it does support it, we have to fade in the incoming app, otherwise we'll reveal it
+ * with the light reveal scrim.
+ */
+ public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
+ mWallpaperSupportsAmbientMode = supportsAmbientMode;
+ }
+
private static class StartKeyguardExitAnimParams {
@WindowManager.TransitionOldType int mTransit;
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java
new file mode 100644
index 000000000000..c8afd72d1dde
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/CollapsedSbFragmentLog.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 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.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/**
+ * A {@link LogBuffer} for
+ * {@link com.android.systemui.statusbar.phone.CollapsedStatusBarFragment}-related messages.
+ */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface CollapsedSbFragmentLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 19193f9eceb2..84c5a571c857 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -100,6 +100,17 @@ public class LogModule {
return factory.create("PrivacyLog", 100);
}
+ /**
+ * Provides a logging buffer for
+ * {@link com.android.systemui.statusbar.phone.CollapsedStatusBarFragment}.
+ */
+ @Provides
+ @SysUISingleton
+ @CollapsedSbFragmentLog
+ public static LogBuffer provideCollapsedSbFragmentLogBuffer(LogBufferFactory factory) {
+ return factory.create("CollapsedSbFragmentLog", 20);
+ }
+
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
@Provides
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 2de40262f42d..c02cc8dda4c4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -102,10 +102,7 @@ import java.util.function.Consumer;
public class NavigationBarView extends FrameLayout implements
NavigationModeController.ModeChangedListener {
final static boolean DEBUG = false;
- final static String TAG = "StatusBar/NavBarView";
-
- // slippery nav bar when everything is disabled, e.g. during setup
- final static boolean SLIPPERY_WHEN_DISABLED = true;
+ final static String TAG = "NavBarView";
final static boolean ALTERNATE_CAR_MODE_UI = false;
private final RegionSamplingHelper mRegionSamplingHelper;
@@ -385,6 +382,12 @@ public class NavigationBarView extends FrameLayout implements
}
}
+ @Override
+ protected boolean onSetAlpha(int alpha) {
+ Log.e(TAG, "onSetAlpha", new Throwable());
+ return super.onSetAlpha(alpha);
+ }
+
public void setAutoHideController(AutoHideController autoHideController) {
mAutoHideController = autoHideController;
}
@@ -1319,6 +1322,7 @@ public class NavigationBarView extends FrameLayout implements
dumpButton(pw, "back", getBackButton());
dumpButton(pw, "home", getHomeButton());
+ dumpButton(pw, "handle", getHomeHandle());
dumpButton(pw, "rcnt", getRecentsButton());
dumpButton(pw, "rota", getRotateSuggestionButton());
dumpButton(pw, "a11y", getAccessibilityButton());
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 3167070e2c3e..fc377c374f53 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -30,6 +30,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_I
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import android.app.StatusBarManager;
import android.app.StatusBarManager.WindowVisibleState;
@@ -46,6 +47,7 @@ import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
import javax.inject.Inject;
@@ -100,7 +102,8 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
mDisplayId = displayId;
mCommandQueue.addCallback(this);
mOverviewProxyService.addCallback(this);
- mNavigationModeController.addListener(this);
+ mEdgeBackGestureHandler.onNavigationModeChanged(
+ mNavigationModeController.addListener(this));
mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
// Set initial state for any listeners
updateSysuiFlags();
@@ -126,6 +129,8 @@ public class TaskbarDelegate implements CommandQueue.Callbacks,
.setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isWindowVisible())
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
+ .setFlag(SYSUI_STATE_SCREEN_PINNING,
+ ActivityManagerWrapper.getInstance().isScreenPinningActive())
.commitUpdate(mDisplayId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
index a0cf44c8bfdc..16b971f876bc 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
@@ -102,6 +102,7 @@ public abstract class PluginsModule {
}
@Provides
+ @Singleton
static PluginManager providesPluginManager(
Context context,
PluginActionManager.Factory instanceManagerFactory,
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
index a626681c0b01..d0aa710ecea6 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
@@ -29,6 +29,7 @@ import android.util.Log
import androidx.annotation.MainThread
import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.appops.AppOpsController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -67,6 +68,7 @@ class PrivacyDialogController(
private val privacyLogger: PrivacyLogger,
private val keyguardStateController: KeyguardStateController,
private val appOpsController: AppOpsController,
+ private val uiEventLogger: UiEventLogger,
@VisibleForTesting private val dialogProvider: DialogProvider
) {
@@ -81,7 +83,8 @@ class PrivacyDialogController(
@Main uiExecutor: Executor,
privacyLogger: PrivacyLogger,
keyguardStateController: KeyguardStateController,
- appOpsController: AppOpsController
+ appOpsController: AppOpsController,
+ uiEventLogger: UiEventLogger
) : this(
permissionManager,
packageManager,
@@ -93,6 +96,7 @@ class PrivacyDialogController(
privacyLogger,
keyguardStateController,
appOpsController,
+ uiEventLogger,
defaultDialogProvider
)
@@ -105,6 +109,7 @@ class PrivacyDialogController(
private val onDialogDismissed = object : PrivacyDialog.OnDialogDismissed {
override fun onDialogDismissed() {
privacyLogger.logPrivacyDialogDismissed()
+ uiEventLogger.log(PrivacyDialogEvent.PRIVACY_DIALOG_DISMISSED)
dialog = null
}
}
@@ -114,6 +119,8 @@ class PrivacyDialogController(
val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS)
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName)
intent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+ uiEventLogger.log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
+ userId, packageName)
privacyLogger.logStartSettingsActivityFromDialog(packageName, userId)
if (!keyguardStateController.isUnlocked) {
// If we are locked, hide the dialog so the user can unlock
@@ -155,7 +162,7 @@ class PrivacyDialogController(
val items = usage.mapNotNull {
val type = filterType(permGroupToPrivacyType(it.permGroupName))
val userInfo = userInfos.firstOrNull { ui -> ui.id == UserHandle.getUserId(it.uid) }
- userInfo?.let { ui ->
+ if (userInfo != null || it.isPhoneCall) {
type?.let { t ->
// Only try to get the app name if we actually need it
val appName = if (it.isPhoneCall) {
@@ -171,10 +178,14 @@ class PrivacyDialogController(
it.attribution,
it.lastAccess,
it.isActive,
- ui.isManagedProfile,
+ // If there's no user info, we're in a phoneCall in secondary user
+ userInfo?.isManagedProfile ?: false,
it.isPhoneCall
)
}
+ } else {
+ // No matching user or phone call
+ null
}
}
uiExecutor.execute {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogEvent.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogEvent.kt
new file mode 100644
index 000000000000..3ecc5a5e5b00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogEvent.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class PrivacyDialogEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "Privacy dialog is clicked by user to go to the app settings page.")
+ PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS(904),
+
+ @UiEvent(doc = "Privacy dialog is dismissed by user.")
+ PRIVACY_DIALOG_DISMISSED(905);
+
+ override fun getId() = _id
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
index fcfa72a82acb..f6c89a9c66a2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
@@ -36,7 +36,7 @@ class FooterActionsControllerBuilder @Inject constructor(
private val activityStarter: ActivityStarter,
private val userManager: UserManager,
private val userInfoController: UserInfoController,
- private val multiUserSwitchController: MultiUserSwitchController,
+ private val multiUserSwitchControllerFactory: MultiUserSwitchController.Factory,
private val deviceProvisionedController: DeviceProvisionedController,
private val falsingManager: FalsingManager,
private val metricsLogger: MetricsLogger,
@@ -60,8 +60,8 @@ class FooterActionsControllerBuilder @Inject constructor(
fun build(): FooterActionsController {
return FooterActionsController(view, qsPanelController, activityStarter, userManager,
- userInfoController, multiUserSwitchController, deviceProvisionedController,
- falsingManager, metricsLogger, tunerService, globalActionsDialog, uiEventLogger,
- showPMLiteButton, buttonsVisibleState)
+ userInfoController, multiUserSwitchControllerFactory.create(view),
+ deviceProvisionedController, falsingManager, metricsLogger, tunerService,
+ globalActionsDialog, uiEventLogger, showPMLiteButton, buttonsVisibleState)
}
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 6e09f22fba63..90254270f3c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -42,6 +42,7 @@ import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.brightness.BrightnessController;
import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
import com.android.systemui.settings.brightness.BrightnessSlider;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.tuner.TunerService;
@@ -60,6 +61,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
private final QSCustomizerController mQsCustomizerController;
private final QSTileRevealController.Factory mQsTileRevealControllerFactory;
private final FalsingManager mFalsingManager;
+ private final CommandQueue mCommandQueue;
private final BrightnessController mBrightnessController;
private final BrightnessSlider mBrightnessSlider;
private final BrightnessMirrorHandler mBrightnessMirrorHandler;
@@ -97,7 +99,8 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
QSTileRevealController.Factory qsTileRevealControllerFactory,
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
- BrightnessSlider.Factory brightnessSliderFactory, FalsingManager falsingManager) {
+ BrightnessSlider.Factory brightnessSliderFactory, FalsingManager falsingManager,
+ CommandQueue commandQueue) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager);
mQsSecurityFooter = qsSecurityFooter;
@@ -105,6 +108,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
mQsCustomizerController = qsCustomizerController;
mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
mFalsingManager = falsingManager;
+ mCommandQueue = commandQueue;
mQsSecurityFooter.setHostEnvironment(qstileHost);
mBrightnessSlider = brightnessSliderFactory.create(getContext(), mView);
@@ -274,6 +278,14 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
/** */
public void showDetailAdapter(DetailAdapter detailAdapter, int x, int y) {
+ // TODO(b/199296365)
+ // Workaround for opening detail from QQS, when there might not be enough space to
+ // display e.g. in case of multiuser detail from split shade. Currently showing detail works
+ // only for QS (mView below) and that's why expanding panel (thus showing QS instead of QQS)
+ // makes it displayed correctly.
+ if (!isExpanded()) {
+ mCommandQueue.animateExpandSettingsPanel(null);
+ }
mView.showDetailAdapter(true, detailAdapter, new int[]{x, y});
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 9ceeb754f9b7..4e9b0f1e369d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -96,6 +96,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private final UiEventLogger mUiEventLogger;
private final InstanceIdSequence mInstanceIdSequence;
private final CustomTileStatePersister mCustomTileStatePersister;
+ private final FeatureFlags mFeatureFlags;
private final List<Callback> mCallbacks = new ArrayList<>();
private AutoTileManager mAutoTiles;
@@ -123,7 +124,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
UiEventLogger uiEventLogger,
UserTracker userTracker,
SecureSettings secureSettings,
- CustomTileStatePersister customTileStatePersister
+ CustomTileStatePersister customTileStatePersister,
+ FeatureFlags featureFlags
) {
mIconController = iconController;
mContext = context;
@@ -145,6 +147,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mUserTracker = userTracker;
mSecureSettings = secureSettings;
mCustomTileStatePersister = customTileStatePersister;
+ mFeatureFlags = featureFlags;
mainHandler.post(() -> {
// This is technically a hack to avoid circular dependency of
@@ -266,7 +269,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
}
- final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
+ final List<String> tileSpecs = loadTileSpecs(mContext, newValue, mFeatureFlags);
int currentUser = mUserTracker.getUserId();
if (currentUser != mCurrentUser) {
mUserContext = mUserTracker.getUserContext();
@@ -335,7 +338,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
// If we didn't manage to create any tiles, set it to empty (default)
Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
- changeTiles(currentSpecs, loadTileSpecs(mContext, ""));
+ changeTiles(currentSpecs, loadTileSpecs(mContext, "", mFeatureFlags));
} else {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onTilesChanged();
@@ -403,7 +406,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private void changeTileSpecs(Predicate<List<String>> changeFunction) {
final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
- final List<String> tileSpecs = loadTileSpecs(mContext, setting);
+ final List<String> tileSpecs = loadTileSpecs(mContext, setting, mFeatureFlags);
if (changeFunction.test(tileSpecs)) {
saveTilesToSettings(tileSpecs);
}
@@ -492,7 +495,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());
}
- protected static List<String> loadTileSpecs(Context context, String tileList) {
+ protected static List<String> loadTileSpecs(
+ Context context, String tileList, FeatureFlags featureFlags) {
final Resources res = context.getResources();
if (TextUtils.isEmpty(tileList)) {
@@ -525,6 +529,21 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
}
}
}
+ if (featureFlags.isProviderModelSettingEnabled()) {
+ if (!tiles.contains("internet")) {
+ if (tiles.contains("wifi")) {
+ // Replace the WiFi with Internet, and remove the Cell
+ tiles.set(tiles.indexOf("wifi"), "internet");
+ tiles.remove("cell");
+ } else if (tiles.contains("cell")) {
+ // Replace the Cell with Internet
+ tiles.set(tiles.indexOf("cell"), "internet");
+ }
+ } else {
+ tiles.remove("wifi");
+ tiles.remove("cell");
+ }
+ }
return tiles;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 1c20a860a3cb..993bbd039b60 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -63,6 +63,7 @@ public class TileQueryHelper {
private final Executor mBgExecutor;
private final Context mContext;
private final UserTracker mUserTracker;
+ private final FeatureFlags mFeatureFlags;
private TileStateListener mListener;
private boolean mFinished;
@@ -72,12 +73,14 @@ public class TileQueryHelper {
Context context,
UserTracker userTracker,
@Main Executor mainExecutor,
- @Background Executor bgExecutor
+ @Background Executor bgExecutor,
+ FeatureFlags featureFlags
) {
mContext = context;
mMainExecutor = mainExecutor;
mBgExecutor = bgExecutor;
mUserTracker = userTracker;
+ mFeatureFlags = featureFlags;
}
public void setListener(TileStateListener listener) {
@@ -118,6 +121,10 @@ public class TileQueryHelper {
}
final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
+ if (mFeatureFlags.isProviderModelSettingEnabled()) {
+ possibleTiles.remove("cell");
+ possibleTiles.remove("wifi");
+ }
for (String spec : possibleTiles) {
// Only add current and stock tiles that can be created from QSFactoryImpl.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index 386769cd399e..b11420afa529 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -39,7 +39,6 @@ import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.qs.customize.QSCustomizer;
-import com.android.systemui.statusbar.phone.MultiUserSwitch;
import javax.inject.Named;
@@ -82,12 +81,6 @@ public interface QSFragmentModule {
/** */
@Provides
- static MultiUserSwitch providesMultiUserSWitch(QSFooterView qsFooterView) {
- return qsFooterView.findViewById(R.id.multi_user_switch);
- }
-
- /** */
- @Provides
static QSPanel provideQSPanel(@RootView View view) {
return view.findViewById(R.id.quick_settings_panel);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 70f52ad5432d..50e7e43822ea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -58,9 +58,11 @@ import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.DeviceInfoUtils;
+import com.android.settingslib.SignalIcon;
import com.android.settingslib.Utils;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.MobileMappings;
+import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.settingslib.wifi.WifiUtils;
import com.android.systemui.R;
@@ -534,6 +536,14 @@ public class InternetDialogController implements WifiEntry.DisconnectCallback,
}
int resId = mapIconSets(config).get(iconKey).dataContentDescription;
+ final MergedCarrierEntry mergedCarrierEntry =
+ mAccessPointController.getMergedCarrierEntry();
+ if (mergedCarrierEntry != null && mergedCarrierEntry.isDefaultNetwork()) {
+ SignalIcon.MobileIconGroup carrierMergedWifiIconGroup =
+ TelephonyIcons.CARRIER_MERGED_WIFI;
+ resId = carrierMergedWifiIconGroup.dataContentDescription;
+ }
+
return resId != 0
? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
new file mode 100644
index 000000000000..cf34db233b06
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DisableFlagsLogger.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar
+
+import android.app.StatusBarManager.DISABLE_BACK
+import android.app.StatusBarManager.DISABLE_CLOCK
+import android.app.StatusBarManager.DISABLE_EXPAND
+import android.app.StatusBarManager.DISABLE_HOME
+import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
+import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
+import android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP
+import android.app.StatusBarManager.DISABLE_RECENT
+import android.app.StatusBarManager.DISABLE_SEARCH
+import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
+import android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS
+import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
+import android.app.StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS
+import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS
+import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * A singleton that creates concise but readable strings representing the values of the disable
+ * flags for debugging.
+ *
+ * See [CommandQueue.disable] for information about disable flags.
+ *
+ * Note that, for both lists passed in, each flag must have a distinct [DisableFlag.flagIsSetSymbol]
+ * and distinct [DisableFlag.flagNotSetSymbol] within the list. If this isn't true, the logs could
+ * be ambiguous so an [IllegalArgumentException] is thrown.
+ */
+@SysUISingleton
+class DisableFlagsLogger constructor(
+ private val disable1FlagsList: List<DisableFlag>,
+ private val disable2FlagsList: List<DisableFlag>
+) {
+
+ @Inject
+ constructor() : this(defaultDisable1FlagsList, defaultDisable2FlagsList)
+
+ init {
+ if (flagsListHasDuplicateSymbols(disable1FlagsList)) {
+ throw IllegalArgumentException("disable1 flags must have unique symbols")
+ }
+ if (flagsListHasDuplicateSymbols(disable2FlagsList)) {
+ throw IllegalArgumentException("disable2 flags must have unique symbols")
+ }
+ }
+
+ private fun flagsListHasDuplicateSymbols(list: List<DisableFlag>): Boolean {
+ val numDistinctFlagOffStatus = list.map { it.getFlagStatus(0) }.distinct().count()
+ val numDistinctFlagOnStatus = list
+ .map { it.getFlagStatus(Int.MAX_VALUE) }
+ .distinct()
+ .count()
+ return numDistinctFlagOffStatus < list.count() || numDistinctFlagOnStatus < list.count()
+ }
+
+ /**
+ * Returns a string representing the, old, new, and new-after-modification disable flag states,
+ * as well as the differences between each of the states.
+ *
+ * Example:
+ * Old: EnaiHbcRso.qINgr | New: EnaihBcRso.qiNGR (hB.iGR) | New after local modification:
+ * EnaihBcRso.qInGR (.n)
+ *
+ * A capital character signifies the flag is set and a lowercase character signifies that the
+ * flag isn't set. The flag states will be logged in the same order as the passed-in lists.
+ *
+ * The difference between states is written between parentheses, and won't be included if there
+ * is no difference. the new-after-modification state also won't be included if there's no
+ * difference from the new state.
+ *
+ * @param old the disable state that had been previously sent.
+ * @param new the new disable state that has just been sent.
+ * @param newAfterLocalModification the new disable states after a class has locally modified
+ * them. Null if the class does not locally modify.
+ */
+ fun getDisableFlagsString(
+ old: DisableState,
+ new: DisableState,
+ newAfterLocalModification: DisableState? = null
+ ): String {
+ val builder = StringBuilder("Received new disable state. ")
+ builder.append("Old: ")
+ builder.append(getFlagsString(old))
+ builder.append(" | New: ")
+ if (old != new) {
+ builder.append(getFlagsStringWithDiff(old, new))
+ } else {
+ builder.append(getFlagsString(old))
+ }
+
+ if (newAfterLocalModification != null && new != newAfterLocalModification) {
+ builder.append(" | New after local modification: ")
+ builder.append(getFlagsStringWithDiff(new, newAfterLocalModification))
+ }
+
+ return builder.toString()
+ }
+
+ /**
+ * Returns a string representing [new] state, as well as the difference from [old] to [new]
+ * (if there is one).
+ */
+ private fun getFlagsStringWithDiff(old: DisableState, new: DisableState): String {
+ val builder = StringBuilder()
+ builder.append(getFlagsString(new))
+ builder.append(" ")
+ builder.append(getDiffString(old, new))
+ return builder.toString()
+ }
+
+ /**
+ * Returns a string representing the difference between [old] and [new], or an empty string if
+ * there is no difference.
+ *
+ * For example, if old was "abc.DE" and new was "aBC.De", the difference returned would be
+ * "(BC.e)".
+ */
+ private fun getDiffString(old: DisableState, new: DisableState): String {
+ if (old == new) {
+ return ""
+ }
+
+ val builder = StringBuilder("(")
+ disable1FlagsList.forEach {
+ val newSymbol = it.getFlagStatus(new.disable1)
+ if (it.getFlagStatus(old.disable1) != newSymbol) {
+ builder.append(newSymbol)
+ }
+ }
+ builder.append(".")
+ disable2FlagsList.forEach {
+ val newSymbol = it.getFlagStatus(new.disable2)
+ if (it.getFlagStatus(old.disable2) != newSymbol) {
+ builder.append(newSymbol)
+ }
+ }
+ builder.append(")")
+ return builder.toString()
+ }
+
+ /** Returns a string representing the disable flag states, e.g. "EnaihBcRso.qiNGR". */
+ private fun getFlagsString(state: DisableState): String {
+ val builder = StringBuilder("")
+ disable1FlagsList.forEach { builder.append(it.getFlagStatus(state.disable1)) }
+ builder.append(".")
+ disable2FlagsList.forEach { builder.append(it.getFlagStatus(state.disable2)) }
+ return builder.toString()
+ }
+
+ /** A POJO representing each disable flag. */
+ class DisableFlag(
+ private val bitMask: Int,
+ private val flagIsSetSymbol: Char,
+ private val flagNotSetSymbol: Char
+ ) {
+
+ /**
+ * Returns a character representing whether or not this flag is set in [state].
+ *
+ * A capital character signifies the flag is set and a lowercase character signifies that
+ * the flag isn't set.
+ */
+ internal fun getFlagStatus(state: Int): Char =
+ if (0 != state and this.bitMask) this.flagIsSetSymbol
+ else this.flagNotSetSymbol
+ }
+
+ /** POJO to hold [disable1] and [disable2]. */
+ data class DisableState(val disable1: Int, val disable2: Int)
+}
+
+// LINT.IfChange
+private val defaultDisable1FlagsList: List<DisableFlagsLogger.DisableFlag> = listOf(
+ DisableFlagsLogger.DisableFlag(DISABLE_EXPAND, 'E', 'e'),
+ DisableFlagsLogger.DisableFlag(DISABLE_NOTIFICATION_ICONS, 'N', 'n'),
+ DisableFlagsLogger.DisableFlag(DISABLE_NOTIFICATION_ALERTS, 'A', 'a'),
+ DisableFlagsLogger.DisableFlag(DISABLE_SYSTEM_INFO, 'I', 'i'),
+ DisableFlagsLogger.DisableFlag(DISABLE_HOME, 'H', 'h'),
+ DisableFlagsLogger.DisableFlag(DISABLE_BACK, 'B', 'b'),
+ DisableFlagsLogger.DisableFlag(DISABLE_CLOCK, 'C', 'c'),
+ DisableFlagsLogger.DisableFlag(DISABLE_RECENT, 'R', 'r'),
+ DisableFlagsLogger.DisableFlag(DISABLE_SEARCH, 'S', 's'),
+ DisableFlagsLogger.DisableFlag(DISABLE_ONGOING_CALL_CHIP, 'O', 'o')
+)
+
+private val defaultDisable2FlagsList: List<DisableFlagsLogger.DisableFlag> = listOf(
+ DisableFlagsLogger.DisableFlag(DISABLE2_QUICK_SETTINGS, 'Q', 'q'),
+ DisableFlagsLogger.DisableFlag(DISABLE2_SYSTEM_ICONS, 'I', 'i'),
+ DisableFlagsLogger.DisableFlag(DISABLE2_NOTIFICATION_SHADE, 'N', 'n'),
+ DisableFlagsLogger.DisableFlag(DISABLE2_GLOBAL_ACTIONS, 'G', 'g'),
+ DisableFlagsLogger.DisableFlag(DISABLE2_ROTATE_SUGGESTIONS, 'R', 'r')
+)
+// LINT.ThenChange(frameworks/base/core/java/android/app/StatusBarManager.java) \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 445715e138ab..17bcfe73ccd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -66,6 +66,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.ViewClippingUtil;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
@@ -455,7 +456,8 @@ public class KeyguardIndicationController {
new KeyguardIndication.Builder()
.setMessage(mContext.getResources().getString(
com.android.internal.R.string.global_action_logout))
- .setTextColor(mInitialTextColorState)
+ .setTextColor(Utils.getColorAttr(
+ mContext, com.android.internal.R.attr.textColorOnAccent))
.setBackground(mContext.getDrawable(
com.android.systemui.R.drawable.logout_button_background))
.setClickListener((view) -> {
@@ -798,7 +800,9 @@ public class KeyguardIndicationController {
* Show message on the keyguard for how the user can unlock/enter their device.
*/
public void showActionToUnlock() {
- if (mDozing) {
+ if (mDozing
+ && !mKeyguardUpdateMonitor.getUserCanSkipBouncer(
+ KeyguardUpdateMonitor.getCurrentUser())) {
return;
}
@@ -809,7 +813,7 @@ public class KeyguardIndicationController {
String message = mContext.getString(R.string.keyguard_retry);
mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
}
- } else if (mKeyguardUpdateMonitor.isScreenOn()) {
+ } else {
showTransientIndication(mContext.getString(R.string.keyguard_unlock),
false /* isError */, true /* hideOnScreenOff */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 73c4b054fd4e..1530e5238c67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -874,7 +874,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mRow.getImageResolver().purgeCache();
}
- private class RtlEnabledContext extends ContextWrapper {
+ private static class RtlEnabledContext extends ContextWrapper {
private RtlEnabledContext(Context packageContext) {
super(packageContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 6e201048abdb..2c76cfeff733 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -116,7 +116,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
/**
* Mode in which fingerprint unlocks the device or passive auth (ie face auth) unlocks the
- * device while being requested when keyguard is occluded.
+ * device while being requested when keyguard is occluded or showing.
*/
public static final int MODE_UNLOCK_COLLAPSING = 5;
@@ -425,6 +425,11 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
if (!wasDeviceInteractive) {
mPendingShowBouncer = true;
} else {
+ mShadeController.animateCollapsePanels(
+ CommandQueue.FLAG_EXCLUDE_NONE,
+ true /* force */,
+ false /* delayed */,
+ BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR);
mPendingShowBouncer = false;
mKeyguardViewController.notifyKeyguardAuthenticated(
false /* strongAuth */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 96c405866a68..c16cc125aece 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -42,6 +42,7 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.DisableFlagsLogger.DisableState;
import com.android.systemui.statusbar.OperatorNameView;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.StatusBarState;
@@ -93,6 +94,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private DarkIconManager mDarkIconManager;
private final CommandQueue mCommandQueue;
+ private final CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
private final OngoingCallController mOngoingCallController;
private final SystemStatusAnimationScheduler mAnimationScheduler;
@@ -131,6 +133,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
NetworkController networkController,
StatusBarStateController statusBarStateController,
CommandQueue commandQueue,
+ CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
OperatorNameViewController.Factory operatorNameViewControllerFactory
) {
mOngoingCallController = ongoingCallController;
@@ -144,6 +147,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mNetworkController = networkController;
mStatusBarStateController = statusBarStateController;
mCommandQueue = commandQueue;
+ mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
}
@@ -244,7 +248,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
if (displayId != getContext().getDisplayId()) {
return;
}
+
+ int state1BeforeAdjustment = state1;
state1 = adjustDisableFlags(state1);
+
+ mCollapsedStatusBarFragmentLogger.logDisableFlagChange(
+ new DisableState(state1BeforeAdjustment, state2),
+ new DisableState(state1, state2));
+
final int old1 = mDisabled1;
final int diff1 = state1 ^ old1;
final int old2 = mDisabled2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt
new file mode 100644
index 000000000000..3c2b555eea68
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLogger.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.CollapsedSbFragmentLog
+import com.android.systemui.statusbar.DisableFlagsLogger
+import javax.inject.Inject
+
+/** Used by [CollapsedStatusBarFragment] to log messages to a [LogBuffer]. */
+class CollapsedStatusBarFragmentLogger @Inject constructor(
+ @CollapsedSbFragmentLog private val buffer: LogBuffer,
+ private val disableFlagsLogger: DisableFlagsLogger,
+) {
+
+ /** Logs a string representing the old and new disable flag states to [buffer]. */
+ fun logDisableFlagChange(
+ oldState: DisableFlagsLogger.DisableState,
+ newState: DisableFlagsLogger.DisableState) {
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ int1 = oldState.disable1
+ int2 = oldState.disable2
+ long1 = newState.disable1.toLong()
+ long2 = newState.disable2.toLong()
+ },
+ {
+ disableFlagsLogger.getDisableFlagsString(
+ DisableFlagsLogger.DisableState(int1, int2),
+ DisableFlagsLogger.DisableState(long1.toInt(), long2.toInt())
+ )
+ }
+ )
+ }
+}
+
+private const val TAG = "CollapsedSbFragment" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index ab7f9d3bbd25..a8c62fe2984c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -270,13 +270,6 @@ public class DozeParameters implements
}
/**
- * Whether the brightness sensor uses the proximity sensor.
- */
- public boolean brightnessUsesProx() {
- return mResources.getBoolean(R.bool.doze_brightness_uses_prox);
- }
-
- /**
* Callback to listen for DozeParameter changes.
*/
public void addCallback(Callback callback) {
@@ -300,6 +293,7 @@ public class DozeParameters implements
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.print("getAlwaysOn(): "); pw.println(getAlwaysOn());
pw.print("getDisplayStateSupported(): "); pw.println(getDisplayStateSupported());
pw.print("getPulseDuration(): "); pw.println(getPulseDuration());
pw.print("getPulseInDuration(): "); pw.println(getPulseInDuration());
@@ -312,7 +306,6 @@ public class DozeParameters implements
pw.print("getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold());
pw.print("getSelectivelyRegisterSensorsUsingProx(): ");
pw.println(getSelectivelyRegisterSensorsUsingProx());
- pw.print("brightnessUsesProx(): "); pw.println(brightnessUsesProx());
}
interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 8c0dfc5f7ab4..f1d5e0271b96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -252,8 +252,6 @@ public class KeyguardBouncer {
mKeyguardViewController.resetSecurityContainer();
showPromptReason(mBouncerPromptReason);
}
- SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
- SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__SHOWN);
}
};
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 d348954e49bb..e368aad31ac8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -57,6 +57,21 @@ public class KeyguardClockPositionAlgorithm {
private int mUserSwitchPreferredY;
/**
+ * Whether or not there is a custom clock face on keyguard.
+ */
+ private boolean mHasCustomClock;
+
+ /**
+ * Whether or not the NSSL contains any visible notifications.
+ */
+ private boolean mHasVisibleNotifs;
+
+ /**
+ * Height of notification stack: Sum of height of each notification.
+ */
+ private int mNotificationStackHeight;
+
+ /**
* Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher
* avatar.
*/
@@ -113,6 +128,25 @@ public class KeyguardClockPositionAlgorithm {
private boolean mIsSplitShade;
/**
+ * Top location of the udfps icon. This includes the worst case (highest) burn-in
+ * offset that would make the top physically highest on the screen.
+ *
+ * Set to -1 if udfps is not enrolled on the device.
+ */
+ private float mUdfpsTop;
+
+ /**
+ * Bottom y-position of the currently visible clock
+ */
+ private float mClockBottom;
+
+ /**
+ * If true, try to keep clock aligned to the top of the display. Else, assume the clock
+ * is center aligned.
+ */
+ private boolean mIsClockTopAligned;
+
+ /**
* Refreshes the dimension values.
*/
public void loadDimens(Resources res) {
@@ -120,7 +154,7 @@ public class KeyguardClockPositionAlgorithm {
R.dimen.keyguard_status_view_bottom_margin);
mContainerTopPadding =
- res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) / 2;
+ res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
mBurnInPreventionOffsetX = res.getDimensionPixelSize(
R.dimen.burn_in_prevention_offset_x);
mBurnInPreventionOffsetYClock = res.getDimensionPixelSize(
@@ -134,7 +168,7 @@ public class KeyguardClockPositionAlgorithm {
int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY,
float dark, float overStretchAmount, boolean bypassEnabled,
int unlockedStackScrollerPadding, float qsExpansion, int cutoutTopInset,
- boolean isSplitShade) {
+ boolean isSplitShade, float udfpsTop, float clockBottom, boolean isClockTopAligned) {
mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding,
userSwitchHeight);
mPanelExpansion = panelExpansion;
@@ -148,6 +182,9 @@ public class KeyguardClockPositionAlgorithm {
mQsExpansion = qsExpansion;
mCutoutTopInset = cutoutTopInset;
mIsSplitShade = isSplitShade;
+ mUdfpsTop = udfpsTop;
+ mClockBottom = clockBottom;
+ mIsClockTopAligned = isClockTopAligned;
}
public void run(Result result) {
@@ -202,8 +239,34 @@ public class KeyguardClockPositionAlgorithm {
if (clockY - mBurnInPreventionOffsetYClock < mCutoutTopInset) {
shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYClock);
}
- float clockYDark = clockY + burnInPreventionOffsetY() + shift;
+ int burnInPreventionOffsetY = mBurnInPreventionOffsetYClock; // requested offset
+ final boolean hasUdfps = mUdfpsTop > -1;
+ if (hasUdfps && !mIsClockTopAligned) {
+ // ensure clock doesn't overlap with the udfps icon
+ if (mUdfpsTop < mClockBottom) {
+ // sometimes the clock textView extends beyond udfps, so let's just use the
+ // space above the KeyguardStatusView/clock as our burn-in offset
+ burnInPreventionOffsetY = (int) (clockY - mCutoutTopInset) / 2;
+ if (mBurnInPreventionOffsetYClock < burnInPreventionOffsetY) {
+ burnInPreventionOffsetY = mBurnInPreventionOffsetYClock;
+ }
+ shift = -burnInPreventionOffsetY;
+ } else {
+ float upperSpace = clockY - mCutoutTopInset;
+ float lowerSpace = mUdfpsTop - mClockBottom;
+ // center the burn-in offset within the upper + lower space
+ burnInPreventionOffsetY = (int) (lowerSpace + upperSpace) / 2;
+ if (mBurnInPreventionOffsetYClock < burnInPreventionOffsetY) {
+ burnInPreventionOffsetY = mBurnInPreventionOffsetYClock;
+ }
+ shift = (lowerSpace - upperSpace) / 2;
+ }
+ }
+
+ float clockYDark = clockY
+ + burnInPreventionOffsetY(burnInPreventionOffsetY)
+ + shift;
return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mOverStretchAmount);
}
@@ -235,9 +298,7 @@ public class KeyguardClockPositionAlgorithm {
return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
}
- private float burnInPreventionOffsetY() {
- int offset = mBurnInPreventionOffsetYClock;
-
+ private float burnInPreventionOffsetY(int offset) {
return getBurnInOffset(offset * 2, false /* xAxis */) - offset;
}
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 f1cde8a9be7a..a5b5f1cbf1e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -28,7 +28,10 @@ import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
+import androidx.annotation.StyleRes;
+
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.keyguard.KeyguardIndication;
@@ -39,6 +42,12 @@ import java.util.LinkedList;
*/
public class KeyguardIndicationTextView extends TextView {
private static final long MSG_MIN_DURATION_MILLIS_DEFAULT = 1500;
+
+ @StyleRes
+ private static int sStyleId = R.style.TextAppearance_Keyguard_BottomArea;
+ @StyleRes
+ private static int sButtonStyleId = R.style.TextAppearance_Keyguard_BottomArea_Button;
+
private long mNextAnimationTime = 0;
private boolean mAnimationsEnabled = true;
private LinkedList<CharSequence> mMessages = new LinkedList<>();
@@ -136,6 +145,14 @@ public class KeyguardIndicationTextView extends TextView {
public void onAnimationEnd(Animator animator) {
KeyguardIndication info = mKeyguardIndicationInfo.poll();
if (info != null) {
+ // First, update the style.
+ // If a background is set on the text, we don't want shadow on the text
+ if (info.getBackground() != null) {
+ setTextAppearance(sButtonStyleId);
+ } else {
+ setTextAppearance(sStyleId);
+ }
+ setBackground(info.getBackground());
setTextColor(info.getTextColor());
setOnClickListener(info.getClickListener());
setClickable(info.getClickListener() != null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 51eb496c3c2a..abee7a51f91f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -225,19 +225,19 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
}
}
+ // If no one is light, all icons become white.
+ if (numLightStacks == 0) {
+ mStatusBarIconController.getTransitionsController().setIconsDark(
+ false, animateChange());
+ }
+
// If all stacks are light, all icons get dark.
- if (numLightStacks == numStacks) {
+ else if (numLightStacks == numStacks) {
mStatusBarIconController.setIconsDarkArea(null);
mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
}
- // If no one is light, all icons become white.
- else if (numLightStacks == 0) {
- mStatusBarIconController.getTransitionsController().setIconsDark(
- false, animateChange());
- }
-
// Not the same for every stack, magic!
else {
mStatusBarIconController.setIconsDarkArea(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
index f27c7d28df44..3fdf1ceaa2d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
@@ -25,6 +25,7 @@ import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
+import com.android.systemui.qs.FooterActionsView;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -33,7 +34,6 @@ import com.android.systemui.util.ViewController;
import javax.inject.Inject;
/** View Controller for {@link MultiUserSwitch}. */
-@QSScope
public class MultiUserSwitchController extends ViewController<MultiUserSwitch> {
private final UserManager mUserManager;
private final UserSwitcherController mUserSwitcherController;
@@ -60,8 +60,30 @@ public class MultiUserSwitchController extends ViewController<MultiUserSwitch> {
}
};
- @Inject
- public MultiUserSwitchController(MultiUserSwitch view, UserManager userManager,
+ @QSScope
+ public static class Factory {
+ private final UserManager mUserManager;
+ private final UserSwitcherController mUserSwitcherController;
+ private final QSDetailDisplayer mQsDetailDisplayer;
+ private final FalsingManager mFalsingManager;
+
+ @Inject
+ public Factory(UserManager userManager, UserSwitcherController userSwitcherController,
+ QSDetailDisplayer qsDetailDisplayer, FalsingManager falsingManager) {
+ mUserManager = userManager;
+ mUserSwitcherController = userSwitcherController;
+ mQsDetailDisplayer = qsDetailDisplayer;
+ mFalsingManager = falsingManager;
+ }
+
+ public MultiUserSwitchController create(FooterActionsView view) {
+ return new MultiUserSwitchController(view.findViewById(R.id.multi_user_switch),
+ mUserManager, mUserSwitcherController, mQsDetailDisplayer,
+ mFalsingManager);
+ }
+ }
+
+ private MultiUserSwitchController(MultiUserSwitch view, UserManager userManager,
UserSwitcherController userSwitcherController, QSDetailDisplayer qsDetailDisplayer,
FalsingManager falsingManager) {
super(view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index cae85ce10358..b21088a7eb3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -57,6 +57,9 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.SensorLocationInternal;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
@@ -568,6 +571,8 @@ public class NotificationPanelViewController extends PanelViewController {
*/
private float mKeyguardOnlyContentAlpha = 1.0f;
+ private float mUdfpsMaxYBurnInOffset;
+
/**
* Are we currently in gesture navigation
*/
@@ -798,13 +803,13 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
mBigClockContainer = mView.findViewById(R.id.big_clock_container);
- UserAvatarView userAvatarView = null;
+ FrameLayout userAvatarContainer = null;
KeyguardUserSwitcherView keyguardUserSwitcherView = null;
if (mKeyguardUserSwitcherEnabled && mUserManager.isUserSwitcherEnabled()) {
if (mKeyguardQsUserSwitchEnabled) {
ViewStub stub = mView.findViewById(R.id.keyguard_qs_user_switch_stub);
- userAvatarView = (UserAvatarView) stub.inflate();
+ userAvatarContainer = (FrameLayout) stub.inflate();
} else {
ViewStub stub = mView.findViewById(R.id.keyguard_user_switcher_stub);
keyguardUserSwitcherView = (KeyguardUserSwitcherView) stub.inflate();
@@ -820,7 +825,7 @@ public class NotificationPanelViewController extends PanelViewController {
updateViewControllers(
mView.findViewById(R.id.keyguard_status_view),
- userAvatarView,
+ userAvatarContainer,
keyguardUserSwitcherView);
mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
@@ -907,10 +912,11 @@ public class NotificationPanelViewController extends PanelViewController {
mView.getContext());
mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize(
R.dimen.notification_side_paddings);
+ mUdfpsMaxYBurnInOffset = mResources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
}
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
- UserAvatarView userAvatarView,
+ FrameLayout userAvatarView,
KeyguardUserSwitcherView keyguardUserSwitcherView) {
// Re-associate the KeyguardStatusViewController
KeyguardStatusViewComponent statusViewComponent =
@@ -1066,7 +1072,7 @@ public class NotificationPanelViewController extends PanelViewController {
!mKeyguardQsUserSwitchEnabled
&& mKeyguardUserSwitcherEnabled
&& isUserSwitcherEnabled;
- UserAvatarView userAvatarView = (UserAvatarView) reInflateStub(
+ FrameLayout userAvatarView = (FrameLayout) reInflateStub(
R.id.keyguard_qs_user_switch_view /* viewId */,
R.id.keyguard_qs_user_switch_stub /* stubId */,
R.layout.keyguard_qs_user_switch /* layoutId */,
@@ -1265,7 +1271,17 @@ public class NotificationPanelViewController extends PanelViewController {
float darkamount =
mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
? 1.0f : mInterpolatedDarkAmount;
- mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
+
+ float udfpsAodTopLocation = -1f;
+ if (mUpdateMonitor.isUdfpsEnrolled() && mAuthController.getUdfpsProps().size() > 0) {
+ FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0);
+ final SensorLocationInternal location = props.getLocation();
+ udfpsAodTopLocation = location.sensorLocationY - location.sensorRadius
+ - mUdfpsMaxYBurnInOffset;
+ }
+
+ mClockPositionAlgorithm.setup(
+ mStatusBarHeaderHeightKeyguard,
expandedFraction,
mKeyguardStatusViewController.getLockscreenHeight(),
userIconHeight,
@@ -1274,7 +1290,10 @@ public class NotificationPanelViewController extends PanelViewController {
bypassEnabled, getUnlockedStackScrollerPadding(),
computeQsExpansionFraction(),
mDisplayTopInset,
- mShouldUseSplitNotificationShade);
+ mShouldUseSplitNotificationShade,
+ udfpsAodTopLocation,
+ mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard),
+ mKeyguardStatusViewController.isClockTopAligned());
mClockPositionAlgorithm.run(mClockPositionResult);
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
boolean animateClock = animate || mAnimateNextPositionUpdate;
@@ -3546,6 +3565,7 @@ public class NotificationPanelViewController extends PanelViewController {
}
public void dozeTimeTick() {
+ mLockIconViewController.dozeTimeTick();
mKeyguardBottomArea.dozeTimeTick();
mKeyguardStatusViewController.dozeTimeTick();
if (mInterpolatedDarkAmount > 0) {
@@ -3851,10 +3871,6 @@ public class NotificationPanelViewController extends PanelViewController {
mStatusBarKeyguardViewManager.updateKeyguardPosition(event.getX());
}
- if (mLockIconViewController.onTouchEvent(event)) {
- return true;
- }
-
handled |= super.onTouch(v, event);
return !mDozing || mPulsing || handled;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 147ebfb4ab99..03d0bb02e1bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -35,6 +35,7 @@ import android.view.View;
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.LockIconViewController;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dock.DockManager;
@@ -87,6 +88,7 @@ public class NotificationShadeWindowViewController {
private final NotificationShadeDepthController mDepthController;
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private final LockIconViewController mLockIconViewController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private GestureDetector mGestureDetector;
@@ -137,7 +139,8 @@ public class NotificationShadeWindowViewController {
NotificationPanelViewController notificationPanelViewController,
StatusBarWindowView statusBarWindowView,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ LockIconViewController lockIconViewController) {
mInjectionInflationController = injectionInflationController;
mCoordinator = coordinator;
mPulseExpansionHandler = pulseExpansionHandler;
@@ -162,6 +165,7 @@ public class NotificationShadeWindowViewController {
mStatusBarWindowView = statusBarWindowView;
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mLockIconViewController = lockIconViewController;
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -234,6 +238,7 @@ public class NotificationShadeWindowViewController {
if (!isCancel && mService.shouldIgnoreTouch()) {
return false;
}
+
if (isDown) {
setTouchActive(true);
mTouchCancelled = false;
@@ -244,6 +249,7 @@ public class NotificationShadeWindowViewController {
if (mTouchCancelled || mExpandAnimationRunning) {
return false;
}
+
mFalsingCollector.onTouchEvent(ev);
mGestureDetector.onTouchEvent(ev);
mStatusBarKeyguardViewManager.onTouch(ev);
@@ -259,9 +265,17 @@ public class NotificationShadeWindowViewController {
if (isDown) {
mNotificationStackScrollLayoutController.closeControlsIfOutsideTouch(ev);
}
+
if (mStatusBarStateController.isDozing()) {
mService.mDozeScrimController.extendPulse();
}
+ mLockIconViewController.onTouchEvent(
+ ev,
+ () -> mService.wakeUpIfDozing(
+ SystemClock.uptimeMillis(),
+ mView,
+ "LOCK_ICON_TOUCH"));
+
// In case we start outside of the view bounds (below the status bar), we need to
// dispatch
// the touch manually as the view system can't accommodate for touches outside of
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index a09b30f41f49..af556a26e3af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -79,6 +79,8 @@ public class PhoneStatusBarView extends PanelBar {
private int mStatusBarHeight;
@Nullable
private List<StatusBar.ExpansionChangedListener> mExpansionChangedListeners;
+ @Nullable
+ private PanelExpansionStateChangedListener mPanelExpansionStateChangedListener;
private PanelEnabledProvider mPanelEnabledProvider;
@@ -102,6 +104,10 @@ public class PhoneStatusBarView extends PanelBar {
mExpansionChangedListeners = listeners;
}
+ void setPanelExpansionStateChangedListener(PanelExpansionStateChangedListener listener) {
+ mPanelExpansionStateChangedListener = listener;
+ }
+
public void setScrimController(ScrimController scrimController) {
mScrimController = scrimController;
}
@@ -289,11 +295,10 @@ public class PhoneStatusBarView extends PanelBar {
super.panelExpansionChanged(frac, expanded);
updateScrimFraction();
if ((frac == 0 || frac == 1)) {
- if (mBar.getNavigationBarView() != null) {
- mBar.getNavigationBarView().onStatusBarPanelStateChanged();
- }
- if (mBar.getNotificationPanelViewController() != null) {
- mBar.getNotificationPanelViewController().updateSystemUiStateFlags();
+ if (mPanelExpansionStateChangedListener != null) {
+ mPanelExpansionStateChangedListener.onPanelExpansionStateChanged();
+ } else {
+ Log.w(TAG, "No PanelExpansionStateChangedListener provided.");
}
}
@@ -412,4 +417,10 @@ public class PhoneStatusBarView extends PanelBar {
/** Returns true if the panel is enabled and false otherwise. */
boolean panelEnabled();
}
+
+ /** A listener that will be notified when a panel's expansion state may have changed. */
+ public interface PanelExpansionStateChangedListener {
+ /** Called when a panel's expansion state may have changed. */
+ void onPanelExpansionStateChanged();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
new file mode 100644
index 000000000000..28040fd8d8a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import android.graphics.Point
+import android.view.View
+import android.view.ViewGroup
+import com.android.systemui.R
+import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.ViewController
+
+/** Controller for [PhoneStatusBarView]. */
+class PhoneStatusBarViewController(
+ view: PhoneStatusBarView,
+ commandQueue: CommandQueue,
+ statusBarMoveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
+ panelExpansionStateChangedListener: PhoneStatusBarView.PanelExpansionStateChangedListener,
+) : ViewController<PhoneStatusBarView>(view) {
+
+ override fun onViewAttached() {}
+ override fun onViewDetached() {}
+
+ init {
+ mView.setPanelEnabledProvider {
+ commandQueue.panelsEnabled()
+ }
+ mView.setPanelExpansionStateChangedListener(panelExpansionStateChangedListener)
+
+ statusBarMoveFromCenterAnimationController?.let { animationController ->
+ val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
+ val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area)
+
+ val viewCenterProvider = StatusBarViewsCenterProvider()
+ val viewsToAnimate = arrayOf(
+ statusBarLeftSide,
+ systemIconArea
+ )
+
+ animationController.init(viewsToAnimate, viewCenterProvider)
+
+ mView.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ ->
+ val widthChanged = right - left != oldRight - oldLeft
+ if (widthChanged) {
+ statusBarMoveFromCenterAnimationController.onStatusBarWidthChanged()
+ }
+ }
+ }
+ }
+
+ private class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider {
+ override fun getViewCenter(view: View, outPoint: Point) =
+ when (view.id) {
+ R.id.status_bar_left_side -> {
+ // items aligned to the start, return start center point
+ getViewEdgeCenter(view, outPoint, isStart = true)
+ }
+ R.id.system_icon_area -> {
+ // items aligned to the end, return end center point
+ getViewEdgeCenter(view, outPoint, isStart = false)
+ }
+ else -> super.getViewCenter(view, outPoint)
+ }
+
+ /**
+ * Returns start or end (based on [isStart]) center point of the view
+ */
+ private fun getViewEdgeCenter(view: View, outPoint: Point, isStart: Boolean) {
+ val isRtl = view.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
+ val isLeftEdge = isRtl xor isStart
+
+ val viewLocation = IntArray(2)
+ view.getLocationOnScreen(viewLocation)
+
+ val viewX = viewLocation[0]
+ val viewY = viewLocation[1]
+
+ outPoint.x = viewX + if (isLeftEdge) view.height / 2 else view.width - view.height / 2
+ outPoint.y = viewY + view.height / 2
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 685b0625b9a2..6b96f3c23a2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -454,6 +454,7 @@ public class StatusBar extends SystemUI implements
@Nullable
protected LockscreenWallpaper mLockscreenWallpaper;
private final AutoHideController mAutoHideController;
+ private final CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
private final Point mCurrentDisplaySize = new Point();
@@ -536,6 +537,7 @@ public class StatusBar extends SystemUI implements
private final FeatureFlags mFeatureFlags;
private final UnfoldTransitionConfig mUnfoldTransitionConfig;
private final Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimation;
+ private final Lazy<StatusBarMoveFromCenterAnimationController> mMoveFromCenterAnimation;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final MessageRouter mMessageRouter;
private final WallpaperManager mWallpaperManager;
@@ -742,6 +744,7 @@ public class StatusBar extends SystemUI implements
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
+ CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
StatusBarComponent.Factory statusBarComponentFactory,
PluginManager pluginManager,
Optional<LegacySplitScreen> splitScreenOptional,
@@ -768,6 +771,7 @@ public class StatusBar extends SystemUI implements
BrightnessSlider.Factory brightnessSliderFactory,
UnfoldTransitionConfig unfoldTransitionConfig,
Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
+ Lazy<StatusBarMoveFromCenterAnimationController> statusBarUnfoldAnimationController,
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
@@ -840,6 +844,7 @@ public class StatusBar extends SystemUI implements
mNotificationShadeDepthControllerLazy = notificationShadeDepthControllerLazy;
mVolumeComponent = volumeComponent;
mCommandQueue = commandQueue;
+ mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
mStatusBarComponentFactory = statusBarComponentFactory;
mPluginManager = pluginManager;
mSplitScreenOptional = splitScreenOptional;
@@ -860,6 +865,7 @@ public class StatusBar extends SystemUI implements
mBrightnessSliderFactory = brightnessSliderFactory;
mUnfoldTransitionConfig = unfoldTransitionConfig;
mUnfoldLightRevealOverlayAnimation = unfoldLightRevealOverlayAnimation;
+ mMoveFromCenterAnimation = statusBarUnfoldAnimationController;
mOngoingCallController = ongoingCallController;
mAnimationScheduler = animationScheduler;
mStatusBarLocationPublisher = locationPublisher;
@@ -1141,8 +1147,16 @@ public class StatusBar extends SystemUI implements
sendInitialExpansionAmount(listener);
}
+ StatusBarMoveFromCenterAnimationController moveFromCenterAnimation = null;
+ if (mUnfoldTransitionConfig.isEnabled()) {
+ moveFromCenterAnimation = mMoveFromCenterAnimation.get();
+ }
mPhoneStatusBarViewController =
- new PhoneStatusBarViewController(mStatusBarView, mCommandQueue);
+ new PhoneStatusBarViewController(
+ mStatusBarView,
+ mCommandQueue,
+ moveFromCenterAnimation,
+ this::onPanelExpansionStateChanged);
mPhoneStatusBarViewController.init();
mBatteryMeterViewController = new BatteryMeterViewController(
@@ -1207,6 +1221,7 @@ public class StatusBar extends SystemUI implements
mNetworkController,
mStatusBarStateController,
mCommandQueue,
+ mCollapsedStatusBarFragmentLogger,
mOperatorNameViewControllerFactory
),
CollapsedStatusBarFragment.TAG)
@@ -1412,6 +1427,15 @@ public class StatusBar extends SystemUI implements
}
}
+ private void onPanelExpansionStateChanged() {
+ if (getNavigationBarView() != null) {
+ getNavigationBarView().onStatusBarPanelStateChanged();
+ }
+ if (getNotificationPanelViewController() != null) {
+ getNotificationPanelViewController().updateSystemUiStateFlags();
+ }
+ }
+
@NonNull
@Override
public Lifecycle getLifecycle() {
@@ -3388,8 +3412,6 @@ public class StatusBar extends SystemUI implements
mNotificationPanelViewController.collapseWithDuration(duration);
}
-
-
/**
* Updates the light reveal effect to reflect the reason we're waking or sleeping (for example,
* from the power button).
@@ -4280,6 +4302,7 @@ public class StatusBar extends SystemUI implements
mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
+ mKeyguardViewMediator.setWallpaperSupportsAmbientMode(supportsAmbientMode);
}
};
@@ -4387,7 +4410,8 @@ public class StatusBar extends SystemUI implements
@Override
public void onDozeAmountChanged(float linear, float eased) {
if (mFeatureFlags.useNewLockscreenAnimations()
- && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
+ && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)
+ && !mBiometricUnlockController.isWakeAndUnlock()) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index 6a510c92a3a0..5301b2571534 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -57,6 +57,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
@@ -98,6 +99,7 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
private final VibratorHelper mVibratorHelper;
private final Optional<Vibrator> mVibratorOptional;
private final LightBarController mLightBarController;
+ private final DisableFlagsLogger mDisableFlagsLogger;
private final int mDisplayId;
private final boolean mVibrateOnOpening;
private final VibrationEffect mCameraLaunchGestureVibrationEffect;
@@ -134,6 +136,7 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
VibratorHelper vibratorHelper,
Optional<Vibrator> vibratorOptional,
LightBarController lightBarController,
+ DisableFlagsLogger disableFlagsLogger,
@DisplayId int displayId) {
mStatusBar = statusBar;
@@ -159,6 +162,7 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
mVibratorHelper = vibratorHelper;
mVibratorOptional = vibratorOptional;
mLightBarController = lightBarController;
+ mDisableFlagsLogger = disableFlagsLogger;
mDisplayId = displayId;
mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
@@ -267,7 +271,17 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
if (displayId != mDisplayId) {
return;
}
+
+ int state2BeforeAdjustment = state2;
state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
+ Log.d(StatusBar.TAG,
+ mDisableFlagsLogger.getDisableFlagsString(
+ /* old= */ new DisableFlagsLogger.DisableState(
+ mStatusBar.getDisabled1(), mStatusBar.getDisabled2()),
+ /* new= */ new DisableFlagsLogger.DisableState(
+ state1, state2BeforeAdjustment),
+ /* newStateAfterLocalModification= */ new DisableFlagsLogger.DisableState(
+ state1, state2)));
final int old1 = mStatusBar.getDisabled1();
final int diff1 = state1 ^ old1;
@@ -277,43 +291,6 @@ public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
final int diff2 = state2 ^ old2;
mStatusBar.setDisabled2(state2);
- if (StatusBar.DEBUG) {
- Log.d(StatusBar.TAG, String.format("disable1: 0x%08x -> 0x%08x (diff1: 0x%08x)",
- old1, state1, diff1));
- Log.d(StatusBar.TAG, String.format("disable2: 0x%08x -> 0x%08x (diff2: 0x%08x)",
- old2, state2, diff2));
- }
-
- StringBuilder flagdbg = new StringBuilder();
- flagdbg.append("disable<");
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_EXPAND)) ? 'E' : 'e');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_EXPAND)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? 'I' : 'i');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? 'A' : 'a');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? 'S' : 's');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SYSTEM_INFO)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_BACK)) ? 'B' : 'b');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_BACK)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_HOME)) ? 'H' : 'h');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_HOME)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_RECENT)) ? 'R' : 'r');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_RECENT)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_CLOCK)) ? 'C' : 'c');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_CLOCK)) ? '!' : ' ');
- flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SEARCH)) ? 'S' : 's');
- flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SEARCH)) ? '!' : ' ');
- flagdbg.append("> disable2<");
- flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? 'Q' : 'q');
- flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? '!' : ' ');
- flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? 'I' : 'i');
- flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? '!' : ' ');
- flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? 'N' : 'n');
- flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? '!' : ' ');
- flagdbg.append('>');
- Log.d(StatusBar.TAG, flagdbg.toString());
-
if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
mShadeController.animateCollapsePanels();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 4316ccfbb620..77254435b688 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -197,6 +197,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private boolean mLastGesturalNav;
private boolean mLastIsDocked;
private boolean mLastPulsing;
+ private boolean mLastAnimatedToSleep;
private int mLastBiometricMode;
private boolean mQsExpanded;
private boolean mAnimatedToSleep;
@@ -1012,6 +1013,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mLastBiometricMode = mBiometricUnlockController.getMode();
mLastGesturalNav = mGesturalNav;
mLastIsDocked = mIsDocked;
+ mLastAnimatedToSleep = mAnimatedToSleep;
mStatusBar.onKeyguardViewManagerStatesUpdated();
}
@@ -1055,7 +1057,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
boolean keyguardWithGestureNav = (keyguardShowing && !mLastDozing
|| mLastPulsing && !mLastIsDocked) && mLastGesturalNav;
- return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
+ return (!mLastAnimatedToSleep && !keyguardShowing && !hideWhileDozing || mLastBouncerShowing
|| mLastRemoteInputActive || keyguardWithGestureNav
|| mLastGlobalActionsVisible);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
new file mode 100644
index 000000000000..8af03aa2a3be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import android.view.View
+import android.view.WindowManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.ViewCenterProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import javax.inject.Inject
+
+@SysUISingleton
+class StatusBarMoveFromCenterAnimationController @Inject constructor(
+ private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+ private val windowManager: WindowManager
+) {
+
+ private lateinit var moveFromCenterAnimator: UnfoldMoveFromCenterAnimator
+
+ fun init(viewsToAnimate: Array<View>, viewCenterProvider: ViewCenterProvider) {
+ moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(windowManager,
+ viewCenterProvider = viewCenterProvider)
+
+ unfoldTransitionProgressProvider.addCallback(object : TransitionProgressListener {
+ override fun onTransitionStarted() {
+ moveFromCenterAnimator.updateDisplayProperties()
+
+ viewsToAnimate.forEach {
+ moveFromCenterAnimator.registerViewForAnimation(it)
+ }
+ }
+
+ override fun onTransitionFinished() {
+ moveFromCenterAnimator.onTransitionFinished()
+ moveFromCenterAnimator.clearRegisteredViews()
+ }
+
+ override fun onTransitionProgress(progress: Float) {
+ moveFromCenterAnimator.onTransitionProgress(progress)
+ }
+ })
+ }
+
+ fun onStatusBarWidthChanged() {
+ moveFromCenterAnimator.updateViewPositions()
+ }
+}
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 f8120a8a7029..143aaba648da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -8,13 +8,13 @@ import android.content.res.Configuration
import android.database.ContentObserver
import android.os.Handler
import android.provider.Settings
-import com.android.systemui.statusbar.StatusBarState
import android.view.View
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.StatusBarStateControllerImpl
import com.android.systemui.statusbar.notification.AnimatableProperty
import com.android.systemui.statusbar.notification.PropertyAnimator
@@ -61,6 +61,7 @@ class UnlockedScreenOffAnimationController @Inject constructor(
private var shouldAnimateInKeyguard = false
private var lightRevealAnimationPlaying = false
private var aodUiAnimationPlaying = false
+ private var callbacks = HashSet<Callback>()
/**
* The result of our decision whether to play the screen off animation in
@@ -72,11 +73,17 @@ class UnlockedScreenOffAnimationController @Inject constructor(
private val lightRevealAnimator = ValueAnimator.ofFloat(1f, 0f).apply {
duration = LIGHT_REVEAL_ANIMATION_DURATION
interpolator = Interpolators.LINEAR
- addUpdateListener { lightRevealScrim.revealAmount = it.animatedValue as Float }
+ addUpdateListener {
+ lightRevealScrim.revealAmount = it.animatedValue as Float
+ sendUnlockedScreenOffProgressUpdate(
+ 1f - (it.animatedFraction as Float),
+ 1f - (it.animatedValue as Float))
+ }
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationCancel(animation: Animator?) {
lightRevealScrim.revealAmount = 1f
lightRevealAnimationPlaying = false
+ sendUnlockedScreenOffProgressUpdate(0f, 0f)
}
override fun onAnimationEnd(animation: Animator?) {
@@ -243,7 +250,21 @@ class UnlockedScreenOffAnimationController @Inject constructor(
return true
}
- /**
+ fun addCallback(callback: Callback) {
+ callbacks.add(callback)
+ }
+
+ fun removeCallback(callback: Callback) {
+ callbacks.remove(callback)
+ }
+
+ fun sendUnlockedScreenOffProgressUpdate(linear: Float, eased: Float) {
+ callbacks.forEach {
+ it.onUnlockedScreenOffProgressUpdate(linear, eased)
+ }
+ }
+
+/**
* Whether we're doing the light reveal animation or we're done with that and animating in the
* AOD UI.
*/
@@ -262,4 +283,8 @@ class UnlockedScreenOffAnimationController @Inject constructor(
fun isScreenOffLightRevealAnimationPlaying(): Boolean {
return lightRevealAnimationPlaying
}
+
+ interface Callback {
+ fun onUnlockedScreenOffProgressUpdate(linear: Float, eased: Float)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 63ee701425ed..c45068e0171b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -72,6 +72,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.CollapsedStatusBarFragmentLogger;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.DozeScrimController;
import com.android.systemui.statusbar.phone.DozeServiceHost;
@@ -90,6 +91,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
+import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.StatusBarWindowView;
@@ -187,6 +189,7 @@ public interface StatusBarPhoneModule {
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
+ CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
StatusBarComponent.Factory statusBarComponentFactory,
PluginManager pluginManager,
Optional<LegacySplitScreen> splitScreenOptional,
@@ -213,6 +216,7 @@ public interface StatusBarPhoneModule {
BrightnessSlider.Factory brightnessSliderFactory,
UnfoldTransitionConfig unfoldTransitionConfig,
Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
+ Lazy<StatusBarMoveFromCenterAnimationController> statusBarMoveFromCenterAnimation,
OngoingCallController ongoingCallController,
SystemStatusAnimationScheduler animationScheduler,
StatusBarLocationPublisher locationPublisher,
@@ -282,6 +286,7 @@ public interface StatusBarPhoneModule {
dozeScrimController,
volumeComponent,
commandQueue,
+ collapsedStatusBarFragmentLogger,
statusBarComponentFactory,
pluginManager,
splitScreenOptional,
@@ -307,6 +312,7 @@ public interface StatusBarPhoneModule {
brightnessSliderFactory,
unfoldTransitionConfig,
unfoldLightRevealOverlayAnimation,
+ statusBarMoveFromCenterAnimation,
ongoingCallController,
animationScheduler,
locationPublisher,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index 5e70d0dbc418..d838a05135e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -27,6 +27,7 @@ import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
import com.android.keyguard.KeyguardConstants;
import com.android.keyguard.KeyguardVisibilityHelper;
@@ -56,7 +57,7 @@ import javax.inject.Provider;
* Manages the user switch on the Keyguard that is used for opening the QS user panel.
*/
@KeyguardUserSwitcherScope
-public class KeyguardQsUserSwitchController extends ViewController<UserAvatarView> {
+public class KeyguardQsUserSwitchController extends ViewController<FrameLayout> {
private static final String TAG = "KeyguardQsUserSwitchController";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -76,6 +77,7 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
private final KeyguardUserDetailAdapter mUserDetailAdapter;
private NotificationPanelViewController mNotificationPanelViewController;
+ private UserAvatarView mUserAvatarView;
UserSwitcherController.UserRecord mCurrentUser;
// State info for the user switch and keyguard
@@ -111,7 +113,7 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
@Inject
public KeyguardQsUserSwitchController(
- UserAvatarView view,
+ FrameLayout view,
Context context,
@Main Resources resources,
ScreenLifecycle screenLifecycle,
@@ -143,6 +145,7 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
protected void onInit() {
super.onInit();
if (DEBUG) Log.d(TAG, "onInit");
+ mUserAvatarView = mView.findViewById(R.id.kg_multi_user_avatar);
mAdapter = new UserSwitcherController.BaseUserAdapter(mUserSwitcherController) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
@@ -150,11 +153,10 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
}
};
- mView.setOnClickListener(v -> {
+ mUserAvatarView.setOnClickListener(v -> {
if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
return;
}
-
if (isListAnimating()) {
return;
}
@@ -163,7 +165,7 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
openQsUserPanel();
});
- mView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+ mUserAvatarView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
@@ -237,12 +239,12 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
R.string.accessibility_multi_user_switch_switcher);
}
- if (!TextUtils.equals(mView.getContentDescription(), contentDescription)) {
- mView.setContentDescription(contentDescription);
+ if (!TextUtils.equals(mUserAvatarView.getContentDescription(), contentDescription)) {
+ mUserAvatarView.setContentDescription(contentDescription);
}
int userId = mCurrentUser != null ? mCurrentUser.resolveId() : UserHandle.USER_NULL;
- mView.setDrawableWithBadge(getCurrentUserIcon().mutate(), userId);
+ mUserAvatarView.setDrawableWithBadge(getCurrentUserIcon().mutate(), userId);
}
Drawable getCurrentUserIcon() {
@@ -269,7 +271,7 @@ public class KeyguardQsUserSwitchController extends ViewController<UserAvatarVie
* Get the height of the keyguard user switcher view when closed.
*/
public int getUserIconHeight() {
- return mView.getHeight();
+ return mUserAvatarView.getHeight();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
index 22fd93ed10ed..2d47c8f0b577 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NextAlarmControllerImpl.java
@@ -71,7 +71,11 @@ public class NextAlarmControllerImpl extends BroadcastReceiver
if (mNextAlarm != null) {
pw.println(new Date(mNextAlarm.getTriggerTime()));
pw.print(" PendingIntentPkg=");
- pw.println(mNextAlarm.getShowIntent().getCreatorPackage());
+ if (mNextAlarm.getShowIntent() != null) {
+ pw.println(mNextAlarm.getShowIntent().getCreatorPackage());
+ } else {
+ pw.println("showIntent=null");
+ }
} else {
pw.println("null");
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 0130cb2f8cd3..a288d999550a 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -37,6 +37,7 @@ import android.content.IntentFilter;
import android.content.om.FabricatedOverlay;
import android.content.om.OverlayIdentifier;
import android.content.pm.UserInfo;
+import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.Color;
import android.net.Uri;
@@ -47,9 +48,11 @@ import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.TypedValue;
import androidx.annotation.NonNull;
+import com.android.internal.graphics.ColorUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.SystemUI;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -59,6 +62,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.monet.ColorScheme;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -71,6 +75,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -107,6 +112,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
private DeviceProvisionedController mDeviceProvisionedController;
private WallpaperColors mCurrentColors;
private WallpaperManager mWallpaperManager;
+ private ColorScheme mColorScheme;
// If fabricated overlays were already created for the current theme.
private boolean mNeedsOverlayCreation;
// Dominant color extracted from wallpaper, NOT the color used on the overlay
@@ -403,25 +409,47 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
* Return the main theme color from a given {@link WallpaperColors} instance.
*/
protected int getNeutralColor(@NonNull WallpaperColors wallpaperColors) {
- return wallpaperColors.getPrimaryColor().toArgb();
+ return ColorScheme.getSeedColor(wallpaperColors);
}
protected int getAccentColor(@NonNull WallpaperColors wallpaperColors) {
- Color accentCandidate = wallpaperColors.getSecondaryColor();
- if (accentCandidate == null) {
- accentCandidate = wallpaperColors.getTertiaryColor();
- }
- if (accentCandidate == null) {
- accentCandidate = wallpaperColors.getPrimaryColor();
- }
- return accentCandidate.toArgb();
+ return ColorScheme.getSeedColor(wallpaperColors);
}
/**
* Given a color candidate, return an overlay definition.
*/
protected @Nullable FabricatedOverlay getOverlay(int color, int type) {
- return null;
+ boolean nightMode = (mContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+
+ mColorScheme = new ColorScheme(color, nightMode);
+ List<Integer> colorShades = type == ACCENT
+ ? mColorScheme.getAllAccentColors() : mColorScheme.getAllNeutralColors();
+ String name = type == ACCENT ? "accent" : "neutral";
+ int paletteSize = mColorScheme.getAccent1().size();
+ FabricatedOverlay.Builder overlay =
+ new FabricatedOverlay.Builder("com.android.systemui", name, "android");
+ for (int i = 0; i < colorShades.size(); i++) {
+ int luminosity = i % paletteSize;
+ int paletteIndex = i / paletteSize + 1;
+ String resourceName;
+ switch (luminosity) {
+ case 0:
+ resourceName = "android:color/system_" + name + paletteIndex + "_10";
+ break;
+ case 1:
+ resourceName = "android:color/system_" + name + paletteIndex + "_50";
+ break;
+ default:
+ int l = luminosity - 1;
+ resourceName = "android:color/system_" + name + paletteIndex + "_" + l + "00";
+ }
+ overlay.setResourceValue(resourceName, TypedValue.TYPE_INT_COLOR_ARGB8,
+ ColorUtils.setAlphaComponent(colorShades.get(i), 0xFF));
+ }
+
+ return overlay.build();
}
private void updateThemeOverlays() {
@@ -521,7 +549,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
.map(key -> key + " -> " + categoryToPackage.get(key)).collect(
Collectors.joining(", ")));
}
- Runnable overlaysAppliedRunnable = () -> onOverlaysApplied();
+ Runnable overlaysAppliedRunnable = this::onOverlaysApplied;
if (mNeedsOverlayCreation) {
mNeedsOverlayCreation = false;
mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
@@ -544,6 +572,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable {
pw.println("mSecondaryOverlay=" + mSecondaryOverlay);
pw.println("mNeutralOverlay=" + mNeutralOverlay);
pw.println("mIsMonetEnabled=" + mIsMonetEnabled);
+ pw.println("mColorScheme=" + mColorScheme);
pw.println("mNeedsOverlayCreation=" + mNeedsOverlayCreation);
pw.println("mAcceptColorEvents=" + mAcceptColorEvents);
pw.println("mDeferredThemeEvaluation=" + mDeferredThemeEvaluation);
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 2b4b49b82df1..d44fb76f306e 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -220,6 +220,12 @@ public class WalletActivity extends LifecycleActivity implements
}
@Override
+ protected void onStop() {
+ super.onStop();
+ finish();
+ }
+
+ @Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.wallet_activity_options_menu, menu);
return super.onCreateOptionsMenu(menu);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 06b0bb25e01c..88b596c26c0f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -119,6 +119,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
when(mNotificationIcons.getLayoutParams()).thenReturn(
mock(RelativeLayout.LayoutParams.class));
when(mView.getContext()).thenReturn(getContext());
+ when(mView.getResources()).thenReturn(mResources);
when(mView.findViewById(R.id.animatable_clock_view)).thenReturn(mClockView);
when(mView.findViewById(R.id.animatable_clock_view_large)).thenReturn(mLargeClockView);
@@ -127,7 +128,6 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
when(mLargeClockView.getContext()).thenReturn(getContext());
when(mView.isAttachedToWindow()).thenReturn(true);
- when(mResources.getString(anyInt())).thenReturn("h:mm");
when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mController = new KeyguardClockSwitchController(
mView,
@@ -226,7 +226,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
}
@Test
- public void testDetachRemovesSmartspaceView() {
+ public void testDetachDisconnectsSmartspace() {
when(mSmartspaceController.isEnabled()).thenReturn(true);
when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView);
mController.init();
@@ -237,7 +237,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
- verify(mView).removeView(mFakeSmartspaceView);
+ verify(mSmartspaceController).disconnect();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
index b6d1e42a4763..619d48d1e306 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -257,9 +258,10 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
componentInfo,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
true /* resetLockoutRequiresHardwareAuthToken */,
- 540 /* sensorLocationX */,
- 1600 /* sensorLocationY */,
- 100 /* sensorRadius */);
+ List.of(new SensorLocationInternal("" /* displayId */,
+ 540 /* sensorLocationX */,
+ 1600 /* sensorLocationY */,
+ 100 /* sensorRadius */)));
}
public class TestableView extends AuthBiometricFaceToFingerprintView {
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 8f5eefcff186..f2f0029708ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -19,16 +19,23 @@ package com.android.systemui.biometrics
import android.graphics.PointF
import android.hardware.biometrics.BiometricSourceType
import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.keyguard.WakefulnessLifecycle
+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.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,11 +61,15 @@ class AuthRippleControllerTest : SysuiTestCase() {
@Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var authController: AuthController
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var bypassController: KeyguardBypassController
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var udfpsControllerProvider: Provider<UdfpsController>
@Mock private lateinit var udfpsController: UdfpsController
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var lightRevealScrim: LightRevealScrim
@Before
fun setUp() {
@@ -71,14 +82,18 @@ class AuthRippleControllerTest : SysuiTestCase() {
authController,
configurationController,
keyguardUpdateMonitor,
+ keyguardStateController,
+ wakefulnessLifecycle,
commandRegistry,
notificationShadeWindowController,
bypassController,
biometricUnlockController,
udfpsControllerProvider,
+ statusBarStateController,
rippleView
)
controller.init()
+ `when`(statusBar.lightRevealScrim).thenReturn(lightRevealScrim)
}
@Test
@@ -100,7 +115,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
// THEN update sensor location and show ripple
verify(rippleView).setSensorLocation(fpsLocation)
- verify(rippleView).startUnlockedRipple(any(), any())
+ verify(rippleView).startUnlockedRipple(any())
}
@Test
@@ -121,7 +136,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */)
// THEN no ripple
- verify(rippleView, never()).startUnlockedRipple(any(), any())
+ verify(rippleView, never()).startUnlockedRipple(any())
}
@Test
@@ -142,7 +157,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */)
// THEN no ripple
- verify(rippleView, never()).startUnlockedRipple(any(), any())
+ verify(rippleView, never()).startUnlockedRipple(any())
}
@Test
@@ -166,7 +181,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
// THEN show ripple
verify(rippleView).setSensorLocation(faceLocation)
- verify(rippleView).startUnlockedRipple(any(), any())
+ verify(rippleView).startUnlockedRipple(any())
}
@Test
@@ -186,7 +201,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */)
// THEN no ripple
- verify(rippleView, never()).startUnlockedRipple(any(), any())
+ verify(rippleView, never()).startUnlockedRipple(any())
}
@Test
@@ -201,7 +216,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
0 /* userId */,
BiometricSourceType.FACE /* type */,
false /* isStrongBiometric */)
- verify(rippleView, never()).startUnlockedRipple(any(), any())
+ verify(rippleView, never()).startUnlockedRipple(any())
}
@Test
@@ -216,7 +231,39 @@ class AuthRippleControllerTest : SysuiTestCase() {
0 /* userId */,
BiometricSourceType.FINGERPRINT /* type */,
false /* isStrongBiometric */)
- verify(rippleView, never()).startUnlockedRipple(any(), any())
+ verify(rippleView, never()).startUnlockedRipple(any())
+ }
+
+ @Test
+ fun registersAndDeregisters() {
+ controller.onViewAttached()
+ val captor = ArgumentCaptor
+ .forClass(KeyguardStateController.Callback::class.java)
+ verify(keyguardStateController).addCallback(captor.capture())
+ val captor2 = ArgumentCaptor
+ .forClass(WakefulnessLifecycle.Observer::class.java)
+ verify(wakefulnessLifecycle).addObserver(captor2.capture())
+ controller.onViewDetached()
+ verify(keyguardStateController).removeCallback(any())
+ verify(wakefulnessLifecycle).removeObserver(any())
+ }
+
+ @Test
+ @RunWithLooper(setAsMainLooper = true)
+ fun testAnimatorRunWhenWakeAndUnlock() {
+ val fpsLocation = PointF(5f, 5f)
+ `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
+ controller.onViewAttached()
+ `when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(true)
+ `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
+
+ controller.showRipple(BiometricSourceType.FINGERPRINT)
+ assertTrue("reveal didn't start on keyguardFadingAway",
+ controller.startLightRevealScrimOnKeyguardFadingAway)
+ `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
+ controller.onKeyguardFadingAwayChanged()
+ assertFalse("reveal triggers multiple times",
+ controller.startLightRevealScrimOnKeyguardFadingAway)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
index 977b05ce150c..5fee7fbf8705 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt
@@ -16,6 +16,9 @@
package com.android.systemui.biometrics
+import android.graphics.Rect
+import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
+import android.hardware.biometrics.BiometricOverlayConstants.REASON_UNKNOWN
import android.hardware.biometrics.SensorProperties
import android.hardware.display.DisplayManager
import android.hardware.display.DisplayManagerGlobal
@@ -30,8 +33,12 @@ import android.view.Display
import android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
import android.view.DisplayInfo
import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowInsets
import android.view.WindowManager
+import android.view.WindowMetrics
import androidx.test.filters.SmallTest
+import com.airbnb.lottie.LottieAnimationView
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
@@ -42,9 +49,13 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -66,11 +77,13 @@ class SidefpsControllerTest : SysuiTestCase() {
@Mock
lateinit var windowManager: WindowManager
@Mock
- lateinit var sidefpsView: SidefpsView
+ lateinit var sidefpsView: View
@Mock
lateinit var displayManager: DisplayManager
@Mock
lateinit var handler: Handler
+ @Captor
+ lateinit var overlayCaptor: ArgumentCaptor<View>
private val executor = FakeExecutor(FakeSystemClock())
private lateinit var overlayController: ISidefpsController
@@ -79,6 +92,8 @@ class SidefpsControllerTest : SysuiTestCase() {
@Before
fun setup() {
`when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView)
+ `when`(sidefpsView.findViewById<LottieAnimationView>(eq(R.id.sidefps_animation)))
+ .thenReturn(mock(LottieAnimationView::class.java))
`when`(fingerprintManager.sensorPropertiesInternal).thenReturn(
listOf(
FingerprintSensorPropertiesInternal(
@@ -99,6 +114,9 @@ class SidefpsControllerTest : SysuiTestCase() {
DEFAULT_DISPLAY_ADJUSTMENTS
)
)
+ `when`(windowManager.maximumWindowMetrics).thenReturn(
+ WindowMetrics(Rect(0, 0, 800, 800), WindowInsets.CONSUMED)
+ )
sideFpsController = SidefpsController(
mContext, layoutInflater, fingerprintManager, windowManager, executor,
@@ -112,13 +130,61 @@ class SidefpsControllerTest : SysuiTestCase() {
@Test
fun testSubscribesToOrientationChangesWhenShowingOverlay() {
- overlayController.show()
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
executor.runAllReady()
verify(displayManager).registerDisplayListener(any(), eq(handler))
- overlayController.hide()
+ overlayController.hide(SENSOR_ID)
executor.runAllReady()
verify(displayManager).unregisterDisplayListener(any())
}
+
+ @Test
+ fun testShowsAndHides() {
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ verify(windowManager).addView(overlayCaptor.capture(), any())
+
+ reset(windowManager)
+ overlayController.hide(SENSOR_ID)
+ executor.runAllReady()
+
+ verify(windowManager, never()).addView(any(), any())
+ verify(windowManager).removeView(eq(overlayCaptor.value))
+ }
+
+ @Test
+ fun testShowsOnce() {
+ repeat(5) {
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+ }
+
+ verify(windowManager).addView(any(), any())
+ verify(windowManager, never()).removeView(any())
+ }
+
+ @Test
+ fun testHidesOnce() {
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ repeat(5) {
+ overlayController.hide(SENSOR_ID)
+ executor.runAllReady()
+ }
+
+ verify(windowManager).addView(any(), any())
+ verify(windowManager).removeView(any())
+ }
+
+ @Test
+ fun testIgnoredForKeyguard() {
+ overlayController.show(SENSOR_ID, REASON_AUTH_KEYGUARD)
+ executor.runAllReady()
+
+ verify(windowManager, never()).addView(any(), any())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index f5c6f981d101..deabda35aeae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -32,6 +32,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.TypedArray;
+import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.display.DisplayManager;
@@ -57,7 +58,6 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -65,6 +65,7 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.Execution;
@@ -123,8 +124,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
- private KeyguardViewMediator mKeyguardViewMediator;
- @Mock
private IUdfpsOverlayControllerCallback mUdfpsOverlayControllerCallback;
@Mock
private FalsingManager mFalsingManager;
@@ -152,6 +151,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
private ConfigurationController mConfigurationController;
@Mock
private SystemClock mSystemClock;
+ @Mock
+ private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private FakeExecutor mFgExecutor;
@@ -191,6 +192,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view, null))
.thenReturn(mKeyguardView); // for showOverlay REASON_AUTH_FPM_KEYGUARD
when(mEnrollView.getContext()).thenReturn(mContext);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
@@ -221,7 +223,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager,
mDumpManager,
mKeyguardUpdateMonitor,
- mKeyguardViewMediator,
mFalsingManager,
mPowerManager,
mAccessibilityManager,
@@ -235,7 +236,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
mDisplayManager,
mHandler,
mConfigurationController,
- mSystemClock);
+ mSystemClock,
+ mUnlockedScreenOffAnimationController);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -256,7 +258,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void dozeTimeTick() throws RemoteException {
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
mUdfpsController.dozeTimeTick();
verify(mUdfpsView).dozeTimeTick();
@@ -271,7 +273,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
@@ -294,7 +296,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
@@ -317,7 +319,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
@@ -340,7 +342,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
@@ -362,7 +364,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_MOVE is received
@@ -384,7 +386,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN multiple touches are received
@@ -404,7 +406,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void showUdfpsOverlay_addsViewToWindow() throws RemoteException {
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mWindowManager).addView(eq(mUdfpsView), any());
}
@@ -412,7 +414,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void hideUdfpsOverlay_removesViewFromWindow() throws RemoteException {
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
verify(mWindowManager).removeView(eq(mUdfpsView));
@@ -422,7 +424,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException {
// GIVEN overlay was showing and the udfps bouncer is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true);
// WHEN the overlay is hidden
@@ -436,7 +438,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void testSubscribesToOrientationChangesWhenShowingOverlay() throws Exception {
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler));
@@ -455,7 +457,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
@@ -477,11 +479,12 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void aodInterrupt() throws RemoteException {
- // GIVEN that the overlay is showing and screen is on
+ // GIVEN that the overlay is showing and screen is on and fp is running
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
+ when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
// WHEN fingerprint is requested because of AOD interrupt
mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
// THEN illumination begins
@@ -496,9 +499,10 @@ public class UdfpsControllerTest extends SysuiTestCase {
public void cancelAodInterrupt() throws RemoteException {
// GIVEN AOD interrupt
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
+ when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
when(mUdfpsView.isIlluminationRequested()).thenReturn(true);
// WHEN it is cancelled
@@ -511,9 +515,10 @@ public class UdfpsControllerTest extends SysuiTestCase {
public void aodInterruptTimeout() throws RemoteException {
// GIVEN AOD interrupt
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOn();
mFgExecutor.runAllReady();
+ when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
when(mUdfpsView.isIlluminationRequested()).thenReturn(true);
// WHEN it times out
@@ -527,11 +532,29 @@ public class UdfpsControllerTest extends SysuiTestCase {
public void aodInterruptScreenOff() throws RemoteException {
// GIVEN screen off
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mScreenObserver.onScreenTurnedOff();
mFgExecutor.runAllReady();
// WHEN aod interrupt is received
+ when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
+ mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
+
+ // THEN no illumination because screen is off
+ verify(mUdfpsView, never()).startIllumination(any());
+ }
+
+ @Test
+ public void aodInterrupt_fingerprintNotRunning() throws RemoteException {
+ // GIVEN showing overlay
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD,
+ mUdfpsOverlayControllerCallback);
+ mScreenObserver.onScreenTurnedOn();
+ mFgExecutor.runAllReady();
+
+ // WHEN aod interrupt is received when the fingerprint service isn't running
+ when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
// THEN no illumination because screen is off
@@ -546,7 +569,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
// GIVEN that the overlay is showing
mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
index 88b4039fd2cd..27755edecba6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.biometrics;
import static org.junit.Assert.assertEquals;
import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -61,8 +62,9 @@ public class UdfpsDialogMeasureAdapterTest extends SysuiTestCase {
0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
componentInfo,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
- true /* resetLockoutRequiresHardwareAuthToken */, sensorLocationX, sensorLocationY,
- sensorRadius);
+ true /* resetLockoutRequiresHardwareAuthToken */,
+ List.of(new SensorLocationInternal("" /* displayId */,
+ sensorLocationX, sensorLocationY, sensorRadius)));
assertEquals(970,
UdfpsDialogMeasureAdapter.calculateBottomSpacerHeightForPortrait(
@@ -125,8 +127,9 @@ public class UdfpsDialogMeasureAdapterTest extends SysuiTestCase {
0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
componentInfo,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
- true /* resetLockoutRequiresHardwareAuthToken */, sensorLocationX, sensorLocationY,
- sensorRadius);
+ true /* resetLockoutRequiresHardwareAuthToken */,
+ List.of(new SensorLocationInternal("" /* displayId */,
+ sensorLocationX, sensorLocationY, sensorRadius)));
assertEquals(1205,
UdfpsDialogMeasureAdapter.calculateHorizontalSpacerWidthForLandscape(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index be3e535d8570..2821f3d21606 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -43,6 +43,7 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -89,6 +90,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
@Mock
private ConfigurationController mConfigurationController;
@Mock
+ private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ @Mock
private UdfpsController mUdfpsController;
private FakeSystemClock mSystemClock = new FakeSystemClock();
@@ -121,13 +124,12 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
Optional.of(mStatusBar),
mStatusBarKeyguardViewManager,
mKeyguardUpdateMonitor,
- mExecutor,
mDumpManager,
- mKeyguardViewMediator,
mLockscreenShadeTransitionController,
mConfigurationController,
mSystemClock,
mKeyguardStateController,
+ mUnlockedScreenOffAnimationController,
mUdfpsController);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index a32cb9b6baa9..d6226aa53f67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -43,7 +43,6 @@ public class DozeConfigurationUtil {
when(params.singleTapUsesProx()).thenReturn(true);
when(params.longPressUsesProx()).thenReturn(true);
when(params.getQuickPickupAodDuration()).thenReturn(500);
- when(params.brightnessUsesProx()).thenReturn(true);
doneHolder[0] = true;
return params;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index deb7d31d87a3..e0520b406a0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -18,6 +18,7 @@ package com.android.systemui.doze;
import static com.android.systemui.doze.DozeMachine.State.DOZE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_DOCKED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
@@ -43,7 +44,6 @@ import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
-import android.view.Display;
import androidx.test.filters.SmallTest;
@@ -114,8 +114,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
Optional.of(mSensor.getSensor()), mDozeHost, null /* handler */,
mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager);
-
- mScreen.onScreenState(Display.STATE_ON);
}
@Test
@@ -127,18 +125,9 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
}
@Test
- public void testAod_usesLightSensor() {
- mScreen.onScreenState(Display.STATE_DOZE);
- waitForSensorManager();
-
- mSensor.sendSensorEvent(3);
-
- assertEquals(3, mServiceFake.screenBrightness);
- }
-
- @Test
public void testAod_usesDebugValue() throws Exception {
- mScreen.onScreenState(Display.STATE_DOZE);
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
waitForSensorManager();
Intent intent = new Intent(DozeScreenBrightness.ACTION_AOD_BRIGHTNESS);
@@ -161,34 +150,25 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
}
@Test
- public void testAodDocked_doNotSelectivelyUseProx_usesLightSensor() {
- // GIVEN the device doesn't need to selectively register for prox sensors and
- // brightness sensor uses prox
- when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(false);
- when(mDozeParameters.brightnessUsesProx()).thenReturn(true);
-
+ public void doze_doesNotUseLightSensor() {
// GIVEN the device is docked and the display state changes to ON
- when(mDockManager.isDocked()).thenReturn(true);
- mScreen.onScreenState(Display.STATE_ON);
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE);
waitForSensorManager();
// WHEN new sensor event sent
mSensor.sendSensorEvent(3);
- // THEN brightness is updated
- assertEquals(3, mServiceFake.screenBrightness);
+ // THEN brightness is NOT changed, it's set to the default brightness
+ assertNotSame(3, mServiceFake.screenBrightness);
+ assertEquals(DEFAULT_BRIGHTNESS, mServiceFake.screenBrightness);
}
@Test
- public void testAodDocked_brightnessDoesNotUseProx_usesLightSensor() {
- // GIVEN the device doesn't need to selectively register for prox sensors but
- // the brightness sensor doesn't use prox
- when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(true);
- when(mDozeParameters.brightnessUsesProx()).thenReturn(false);
-
+ public void aod_usesLightSensor() {
// GIVEN the device is docked and the display state changes to ON
- when(mDockManager.isDocked()).thenReturn(true);
- mScreen.onScreenState(Display.STATE_ON);
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
waitForSensorManager();
// WHEN new sensor event sent
@@ -198,34 +178,25 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
assertEquals(3, mServiceFake.screenBrightness);
}
-
@Test
- public void testAodDocked_noProx_brightnessUsesProx_doNotUseLightSensor() {
- final int startBrightness = mServiceFake.screenBrightness;
-
- // GIVEN the device needs to selectively register for prox sensors and
- // the brightness sensor uses prox
- when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(true);
- when(mDozeParameters.brightnessUsesProx()).thenReturn(true);
-
- // GIVEN the device is docked and the display state is on
- when(mDockManager.isDocked()).thenReturn(true);
- mScreen.onScreenState(Display.STATE_ON);
+ public void docked_usesLightSensor() {
+ // GIVEN the device is docked and the display state changes to ON
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+ mScreen.transitionTo(DOZE_AOD, DOZE_AOD_DOCKED);
waitForSensorManager();
// WHEN new sensor event sent
mSensor.sendSensorEvent(3);
- // THEN brightness is NOT changed
- assertNotSame(3, mServiceFake.screenBrightness);
- assertEquals(startBrightness, mServiceFake.screenBrightness);
+ // THEN brightness is updated
+ assertEquals(3, mServiceFake.screenBrightness);
}
@Test
public void testPausingAod_doesNotResetBrightness() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
mSensor.sendSensorEvent(1);
@@ -266,18 +237,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
}
@Test
- public void testOnScreenStateSetBeforeTransition_stillRegistersSensor() {
- mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
- mScreen.onScreenState(Display.STATE_DOZE);
- mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- waitForSensorManager();
-
- mSensor.sendSensorEvent(1);
-
- assertEquals(1, mServiceFake.screenBrightness);
- }
-
- @Test
public void testNullSensor() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
Optional.empty() /* sensor */, mDozeHost, null /* handler */,
@@ -287,15 +246,12 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
- mScreen.onScreenState(Display.STATE_DOZE);
- mScreen.onScreenState(Display.STATE_OFF);
}
@Test
public void testNoBrightnessDeliveredAfterFinish() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- mScreen.onScreenState(Display.STATE_DOZE);
mScreen.transitionTo(DOZE_AOD, FINISH);
waitForSensorManager();
@@ -308,7 +264,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
public void testNonPositiveBrightness_keepsPreviousBrightnessAndScrim() {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
mSensor.sendSensorEvent(1);
@@ -322,7 +277,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
public void pausingAod_unblanksAfterSensor() {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
mSensor.sendSensorEvent(2);
@@ -334,7 +288,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
reset(mDozeHost);
mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
- mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
mSensor.sendSensorEvent(2);
verify(mDozeHost).setAodDimmingScrim(eq(0f));
@@ -344,7 +297,6 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
public void pausingAod_unblanksIfSensorWasAlwaysReady() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
- mScreen.onScreenState(Display.STATE_DOZE);
waitForSensorManager();
mSensor.sendSensorEvent(2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index 41d7fd64fe7a..150ab7700e4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -21,6 +21,7 @@ import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_DOCKED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE;
import static com.android.systemui.doze.DozeMachine.State.FINISH;
@@ -34,6 +35,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -45,6 +47,8 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.wakelock.WakeLockFake;
import com.android.systemui.utils.os.FakeHandler;
@@ -56,6 +60,8 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import javax.inject.Provider;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DozeScreenStateTest extends SysuiTestCase {
@@ -68,17 +74,29 @@ public class DozeScreenStateTest extends SysuiTestCase {
private DozeParameters mDozeParameters;
private WakeLockFake mWakeLock;
private DozeScreenState mScreen;
+ @Mock
+ private Provider<UdfpsController> mUdfpsControllerProvider;
+ @Mock
+ private AuthController mAuthController;
+ @Mock
+ private UdfpsController mUdfpsController;
+ @Mock
+ private DozeLog mDozeLog;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+ when(mUdfpsControllerProvider.get()).thenReturn(mUdfpsController);
+ when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true);
+ when(mUdfpsController.isFingerDown()).thenReturn(false);
+
mServiceFake = new DozeServiceFake();
mHandlerFake = new FakeHandler(Looper.getMainLooper());
mWakeLock = new WakeLockFake();
mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeHost, mDozeParameters,
- mWakeLock);
+ mWakeLock, mAuthController, mUdfpsControllerProvider, mDozeLog);
}
@Test
@@ -233,4 +251,56 @@ public class DozeScreenStateTest extends SysuiTestCase {
assertEquals(Display.STATE_OFF, mServiceFake.screenState);
}
+ @Test
+ public void testDelayEnterDozeScreenState_whenUdfpsFingerDown() {
+ // GIVEN AOD is initialized
+ when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
+ mHandlerFake.setMode(QUEUEING);
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mHandlerFake.dispatchQueuedMessages();
+
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+
+ // WHEN udfps is activated (fingerDown)
+ when(mUdfpsController.isFingerDown()).thenReturn(true);
+ mHandlerFake.dispatchQueuedMessages();
+
+ // THEN the display screen state doesn't immediately change
+ assertEquals(Display.STATE_ON, mServiceFake.screenState);
+
+ // WHEN udfpsController finger is no longer down and the queued messages are run
+ when(mUdfpsController.isFingerDown()).thenReturn(false);
+ mHandlerFake.dispatchQueuedMessages();
+
+ // THEN the display screen state will change
+ assertEquals(Display.STATE_DOZE_SUSPEND, mServiceFake.screenState);
+ }
+
+ @Test
+ public void testDelayExitPulsingScreenState_whenUdfpsFingerDown() {
+ // GIVEN we're pulsing
+ when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
+ mHandlerFake.setMode(QUEUEING);
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+ mScreen.transitionTo(DOZE_AOD, DOZE_REQUEST_PULSE);
+ mScreen.transitionTo(DOZE_REQUEST_PULSE, DOZE_PULSING);
+ mScreen.transitionTo(DOZE_PULSING, DOZE_PULSE_DONE);
+ mHandlerFake.dispatchQueuedMessages();
+
+ // WHEN udfps is activated while are transitioning back to DOZE_AOD
+ mScreen.transitionTo(DOZE_PULSE_DONE, DOZE_AOD);
+ when(mUdfpsController.isFingerDown()).thenReturn(true);
+ mHandlerFake.dispatchQueuedMessages();
+
+ // THEN the display screen state doesn't immediately change
+ assertEquals(Display.STATE_ON, mServiceFake.screenState);
+
+ // WHEN udfpsController finger is no longer down and the queued messages are run
+ when(mUdfpsController.isFingerDown()).thenReturn(false);
+ mHandlerFake.dispatchQueuedMessages();
+
+ // THEN the display screen state will change
+ assertEquals(Display.STATE_DOZE_SUSPEND, mServiceFake.screenState);
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 10997fab081f..9577c7a2d6fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -45,6 +45,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.sensors.AsyncSensorManager;
@@ -83,6 +84,8 @@ public class DozeTriggersTest extends SysuiTestCase {
private AuthController mAuthController;
@Mock
private UiEventLogger mUiEventLogger;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
private DozeTriggers mTriggers;
private FakeSensorManager mSensors;
@@ -114,7 +117,7 @@ public class DozeTriggersTest extends SysuiTestCase {
mTriggers = new DozeTriggers(mContext, mHost, config, dozeParameters,
asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(),
- mAuthController, mExecutor, mUiEventLogger);
+ mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController);
mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
@@ -217,6 +220,32 @@ public class DozeTriggersTest extends SysuiTestCase {
}
@Test
+ public void testPickupGesture() {
+ // GIVEN device is in doze (screen blank, but running doze sensors)
+ when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+
+ // WHEN the pick up gesture is triggered and keyguard isn't occluded
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+ mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
+
+ // THEN wakeup
+ verify(mMachine).wakeUp();
+ }
+
+ @Test
+ public void testPickupGestureDroppedKeyguardOccluded() {
+ // GIVEN device is in doze (screen blank, but running doze sensors)
+ when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+
+ // WHEN the pick up gesture is triggered and keyguard IS occluded
+ when(mKeyguardStateController.isOccluded()).thenReturn(true);
+ mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
+
+ // THEN never wakeup
+ verify(mMachine, never()).wakeUp();
+ }
+
+ @Test
public void testOnSensor_Fingerprint() {
// GIVEN dozing state
when(mMachine.getState()).thenReturn(DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index d279bbb02cee..af6dee76139e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard;
import static junit.framework.Assert.assertEquals;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -25,6 +26,7 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PointF;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
@@ -49,6 +51,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.airbnb.lottie.LottieAnimationView;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -80,6 +84,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
private @Mock DelayableExecutor mDelayableExecutor;
private @Mock Vibrator mVibrator;
private @Mock AuthRippleController mAuthRippleController;
+ private @Mock LottieAnimationView mAodFp;
private LockIconViewController mLockIconViewController;
@@ -101,6 +106,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
when(mLockIconView.getContext()).thenReturn(mContext);
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
+ when(mLockIconView.findViewById(anyInt())).thenReturn(mAodFp);
mLockIconViewController = new LockIconViewController(
mLockIconView,
@@ -121,7 +127,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
@Test
public void testUpdateFingerprintLocationOnInit() {
- // GIVEN fp sensor location is available pre-init
+ // GIVEN fp sensor location is available pre-attached
final PointF udfpsLocation = new PointF(50, 75);
final int radius = 33;
final FingerprintSensorPropertiesInternal fpProps =
@@ -132,14 +138,15 @@ public class LockIconViewControllerTest extends SysuiTestCase {
/* component info */ new ArrayList<>(),
/* sensorType */ 3,
/* resetLockoutRequiresHwToken */ false,
- (int) udfpsLocation.x, (int) udfpsLocation.y, radius);
+ List.of(new SensorLocationInternal("" /* displayId */,
+ (int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
// WHEN lock icon view controller is initialized and attached
mLockIconViewController.init();
captureAttachListener();
- mAttachListener.onViewAttachedToWindow(null);
+ mAttachListener.onViewAttachedToWindow(mLockIconView);
// THEN lock icon view location is updated with the same coordinates as fpProps
verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius));
@@ -152,8 +159,10 @@ public class LockIconViewControllerTest extends SysuiTestCase {
when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
when(mAuthController.getUdfpsProps()).thenReturn(null);
mLockIconViewController.init();
+ captureAttachListener();
+ mAttachListener.onViewAttachedToWindow(mLockIconView);
- // GIVEN fp sensor location is available post-init
+ // GIVEN fp sensor location is available post-atttached
captureAuthControllerCallback();
final PointF udfpsLocation = new PointF(50, 75);
final int radius = 33;
@@ -165,7 +174,8 @@ public class LockIconViewControllerTest extends SysuiTestCase {
/* component info */ new ArrayList<>(),
/* sensorType */ 3,
/* resetLockoutRequiresHwToken */ false,
- (int) udfpsLocation.x, (int) udfpsLocation.y, radius);
+ List.of(new SensorLocationInternal("" /* displayId */,
+ (int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
new file mode 100644
index 000000000000..bc86ef98c6fe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.monet;
+
+import android.app.WallpaperColors;
+import android.graphics.Color;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ColorSchemeTest extends SysuiTestCase {
+ @Test
+ public void testFilterTransparency() {
+ ColorScheme colorScheme = new ColorScheme(Color.TRANSPARENT, false /* darkTheme */);
+ Assert.assertEquals(colorScheme.getAllAccentColors(),
+ new ColorScheme(0xFF1b6ef3, false).getAllAccentColors());
+ }
+
+ @Test
+ public void testDontFilterOpaque() {
+ ColorScheme colorScheme = new ColorScheme(0xFFFF0000, false /* darkTheme */);
+ Assert.assertNotEquals(colorScheme.getAllAccentColors(),
+ new ColorScheme(0xFF1b6ef3, false).getAllAccentColors());
+ }
+
+ @Test
+ public void testUniqueColors() {
+ WallpaperColors wallpaperColors = new WallpaperColors(Color.valueOf(0xffaec00a),
+ Color.valueOf(0xffaec00a), Color.valueOf(0xffaec00a));
+
+ List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
+ Assert.assertEquals(rankedSeedColors, List.of(0xffaec00a));
+ }
+
+
+ @Test
+ public void testFiltersInvalidColors() {
+ WallpaperColors wallpaperColors = new WallpaperColors(Color.valueOf(0xff5e7ea2),
+ Color.valueOf(0xff5e7ea2), Color.valueOf(0xff000000));
+
+ List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
+ Assert.assertEquals(rankedSeedColors, List.of(0xff5e7ea2));
+ }
+
+ @Test
+ public void testInvalidColorBecomesGBlue() {
+ WallpaperColors wallpaperColors = new WallpaperColors(Color.valueOf(0xff000000), null,
+ null);
+
+ List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
+ Assert.assertEquals(rankedSeedColors, List.of(0xFF1b6ef3));
+ }
+
+ @Test
+ public void testDontFilterRRGGBB() {
+ ColorScheme colorScheme = new ColorScheme(0xFF0000, false /* darkTheme */);
+ Assert.assertEquals(colorScheme.getAllAccentColors(),
+ new ColorScheme(0xFFFF0000, false).getAllAccentColors());
+ }
+
+ @Test
+ public void testNoPopulationSignal() {
+ WallpaperColors wallpaperColors = new WallpaperColors(Color.valueOf(0xffaec00a),
+ Color.valueOf(0xffbe0000), Color.valueOf(0xffcc040f));
+
+ List<Integer> rankedSeedColors = ColorScheme.getSeedColors(wallpaperColors);
+ Assert.assertEquals(rankedSeedColors, List.of(0xffaec00a, 0xffbe0000, 0xffcc040f));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
index 03248f7e3a70..511848d8a3af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -22,11 +22,13 @@ import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.UserInfo
+import android.os.Process.SYSTEM_UID
import android.os.UserHandle
import android.permission.PermGroupUsage
import android.permission.PermissionManager
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.appops.AppOpsController
import com.android.systemui.plugins.ActivityStarter
@@ -53,6 +55,7 @@ import org.mockito.Mockito.`when`
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -96,6 +99,8 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
private lateinit var activityStartedCaptor: ArgumentCaptor<ActivityStarter.Callback>
@Captor
private lateinit var intentCaptor: ArgumentCaptor<Intent>
+ @Mock
+ private lateinit var uiEventLogger: UiEventLogger
private val backgroundExecutor = FakeExecutor(FakeSystemClock())
private val uiExecutor = FakeExecutor(FakeSystemClock())
@@ -136,6 +141,7 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
privacyLogger,
keyguardStateController,
appOpsController,
+ uiEventLogger,
dialogProvider
)
}
@@ -550,6 +556,49 @@ class PrivacyDialogControllerTest : SysuiTestCase() {
verify(dialog, never()).dismiss()
}
+ @Test
+ fun testCallOnSecondaryUser() {
+ // Calls happen in
+ val usage = createMockPermGroupUsage(uid = SYSTEM_UID, isPhoneCall = true)
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
+ `when`(userTracker.userProfiles).thenReturn(listOf(
+ UserInfo(ENT_USER_ID, "", 0)
+ ))
+
+ controller.showDialog(context)
+ exhaustExecutors()
+
+ verify(dialog).show()
+ }
+
+ @Test
+ fun testStartActivityLogs() {
+ val usage = createMockPermGroupUsage()
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
+ controller.showDialog(context)
+ exhaustExecutors()
+
+ dialogProvider.starter?.invoke(TEST_PACKAGE_NAME, USER_ID)
+ verify(uiEventLogger).log(PrivacyDialogEvent.PRIVACY_DIALOG_ITEM_CLICKED_TO_APP_SETTINGS,
+ USER_ID, TEST_PACKAGE_NAME)
+ }
+
+ @Test
+ fun testDismissedDialogLogs() {
+ val usage = createMockPermGroupUsage()
+ `when`(permissionManager.getIndicatorAppOpUsageData(anyBoolean())).thenReturn(listOf(usage))
+ controller.showDialog(context)
+ exhaustExecutors()
+
+ verify(dialog).addOnDismissListener(capture(dialogDismissedCaptor))
+
+ dialogDismissedCaptor.value.onDialogDismissed()
+
+ controller.dismissDialog()
+
+ verify(uiEventLogger, times(1)).log(PrivacyDialogEvent.PRIVACY_DIALOG_DISMISSED)
+ }
+
private fun exhaustExecutors() {
FakeExecutor.exhaustExecutors(backgroundExecutor, uiExecutor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 109721f5b096..12c0d5379a20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -43,6 +43,7 @@ import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -95,6 +96,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
private KeyguardBypassController mBypassController;
@Mock
private FalsingManager mFalsingManager;
+ @Mock
+ private FeatureFlags mFeatureFlags;
public QSFragmentTest() {
super(QSFragment.class);
@@ -136,7 +139,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
() -> mock(AutoTileManager.class), mock(DumpManager.class),
mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
- mock(SecureSettings.class), mock(CustomTileStatePersister.class));
+ mock(SecureSettings.class), mock(CustomTileStatePersister.class), mFeatureFlags);
qs.setHost(host);
qs.setListening(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index 1a87975f0e4d..a21f4887038c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -42,6 +42,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.brightness.BrightnessController;
import com.android.systemui.settings.brightness.BrightnessSlider;
import com.android.systemui.settings.brightness.ToggleSlider;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.animation.DisappearParameters;
@@ -94,6 +95,8 @@ public class QSPanelControllerTest extends SysuiTestCase {
QSTileView mQSTileView;
@Mock
PagedTileLayout mPagedTileLayout;
+ @Mock
+ CommandQueue mCommandQueue;
FalsingManagerFake mFalsingManager = new FalsingManagerFake();
private QSPanelController mController;
@@ -121,7 +124,7 @@ public class QSPanelControllerTest extends SysuiTestCase {
mQSTileHost, mQSCustomizerController, true, mMediaHost,
mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
- mFalsingManager
+ mFalsingManager, mCommandQueue
);
mController.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 6c7f770f9482..6bb2b9dda593 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -121,6 +121,8 @@ public class QSTileHostTest extends SysuiTestCase {
private SecureSettings mSecureSettings;
@Mock
private CustomTileStatePersister mCustomTileStatePersister;
+ @Mock
+ private FeatureFlags mFeatureFlags;
private Handler mHandler;
private TestableLooper mLooper;
@@ -138,8 +140,9 @@ public class QSTileHostTest extends SysuiTestCase {
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
- mSecureSettings, mCustomTileStatePersister);
+ mSecureSettings, mCustomTileStatePersister, mFeatureFlags);
setUpTileFactory();
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false);
}
private void setUpTileFactory() {
@@ -167,13 +170,13 @@ public class QSTileHostTest extends SysuiTestCase {
@Test
public void testLoadTileSpecs_emptySetting() {
- List<String> tiles = QSTileHost.loadTileSpecs(mContext, "");
+ List<String> tiles = QSTileHost.loadTileSpecs(mContext, "", mFeatureFlags);
assertFalse(tiles.isEmpty());
}
@Test
public void testLoadTileSpecs_nullSetting() {
- List<String> tiles = QSTileHost.loadTileSpecs(mContext, null);
+ List<String> tiles = QSTileHost.loadTileSpecs(mContext, null, mFeatureFlags);
assertFalse(tiles.isEmpty());
}
@@ -187,6 +190,55 @@ public class QSTileHostTest extends SysuiTestCase {
}
@Test
+ public void testRemoveWifiAndCellularWithoutInternet() {
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2");
+
+ assertEquals("internet", mQSTileHost.mTileSpecs.get(0));
+ assertEquals("spec1", mQSTileHost.mTileSpecs.get(1));
+ assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
+ }
+
+ @Test
+ public void testRemoveWifiAndCellularWithInternet() {
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "wifi, spec1, cell, spec2, internet");
+
+ assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+ assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
+ assertEquals("internet", mQSTileHost.mTileSpecs.get(2));
+ }
+
+ @Test
+ public void testRemoveWifiWithoutInternet() {
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, wifi, spec2");
+
+ assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+ assertEquals("internet", mQSTileHost.mTileSpecs.get(1));
+ assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
+ }
+
+ @Test
+ public void testRemoveCellWithInternet() {
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1, spec2, cell, internet");
+
+ assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+ assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
+ assertEquals("internet", mQSTileHost.mTileSpecs.get(2));
+ }
+
+ @Test
+ public void testNoWifiNoCellularNoInternet() {
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(true);
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
+
+ assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+ assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
+ }
+
+ @Test
public void testSpecWithInvalidDoesNotUseDefault() {
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles, "spec1,spec2");
@@ -319,7 +371,7 @@ public class QSTileHostTest extends SysuiTestCase {
@Test
public void testLoadTileSpec_repeated() {
- List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2");
+ List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2", mFeatureFlags);
assertEquals(2, specs.size());
assertEquals("spec1", specs.get(0));
@@ -330,7 +382,7 @@ public class QSTileHostTest extends SysuiTestCase {
public void testLoadTileSpec_repeatedInDefault() {
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles_default, "spec1,spec1");
- List<String> specs = QSTileHost.loadTileSpecs(mContext, "default");
+ List<String> specs = QSTileHost.loadTileSpecs(mContext, "default", mFeatureFlags);
// Remove spurious tiles, like dbg:mem
specs.removeIf(spec -> !"spec1".equals(spec));
@@ -341,7 +393,7 @@ public class QSTileHostTest extends SysuiTestCase {
public void testLoadTileSpec_repeatedDefaultAndSetting() {
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles_default, "spec1");
- List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1");
+ List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1", mFeatureFlags);
// Remove spurious tiles, like dbg:mem
specs.removeIf(spec -> !"spec1".equals(spec));
@@ -379,11 +431,12 @@ public class QSTileHostTest extends SysuiTestCase {
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
UiEventLogger uiEventLogger, UserTracker userTracker,
- SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister) {
+ SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister,
+ FeatureFlags featureFlags) {
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
tunerService, autoTiles, dumpManager, broadcastDispatcher,
Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings,
- customTileStatePersister);
+ customTileStatePersister, featureFlags);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 8546a359b459..018806e816d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -109,6 +109,8 @@ public class TileQueryHelperTest extends SysuiTestCase {
private PackageManager mPackageManager;
@Mock
private UserTracker mUserTracker;
+ @Mock
+ private FeatureFlags mFeatureFlags;
@Captor
private ArgumentCaptor<List<TileQueryHelper.TileInfo>> mCaptor;
@@ -134,12 +136,12 @@ public class TileQueryHelperTest extends SysuiTestCase {
}
}
).when(mQSTileHost).createTile(anyString());
-
+ when(mFeatureFlags.isProviderModelSettingEnabled()).thenReturn(false);
FakeSystemClock clock = new FakeSystemClock();
mMainExecutor = new FakeExecutor(clock);
mBgExecutor = new FakeExecutor(clock);
mTileQueryHelper = new TileQueryHelper(
- mContext, mUserTracker, mMainExecutor, mBgExecutor);
+ mContext, mUserTracker, mMainExecutor, mBgExecutor, mFeatureFlags);
mTileQueryHelper.setListener(mListener);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 2b1840462291..e3045eb2d63f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -43,6 +43,7 @@ import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
@@ -98,6 +99,8 @@ public class TileServicesTest extends SysuiTestCase {
private UserTracker mUserTracker;
@Mock
private SecureSettings mSecureSettings;
+ @Mock
+ private FeatureFlags mFeatureFlags;
@Before
public void setUp() throws Exception {
@@ -119,7 +122,8 @@ public class TileServicesTest extends SysuiTestCase {
mUiEventLogger,
mUserTracker,
mSecureSettings,
- mock(CustomTileStatePersister.class));
+ mock(CustomTileStatePersister.class),
+ mFeatureFlags);
mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher,
mUserTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
index ebc6f2aa6e9a..6a68b71f639b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
@@ -157,6 +157,21 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() {
assertThat(view.translationY).isWithin(0.01f).of(3.75f)
}
+ @Test
+ fun testUpdateViewPositions_viewOnTheLeftAndMovedToTheRight_viewTranslatedToTheLeft() {
+ givenScreen(width = 100, height = 100, rotation = ROTATION_0)
+ val view = createView(x = 20)
+ animator.registerViewForAnimation(view)
+ animator.onTransitionStarted()
+ animator.onTransitionProgress(0.5f)
+ view.updateMock(x = 80) // view moved from the left side to the right
+
+ animator.updateViewPositions()
+
+ // Negative translationX -> translated to the left
+ assertThat(view.translationX).isWithin(0.1f).of(-5.25f)
+ }
+
private fun createView(
x: Int = 0,
y: Int = 0,
@@ -176,7 +191,30 @@ class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() {
whenever(view.width).thenReturn(width)
whenever(view.height).thenReturn(height)
- return view.apply {
+ view.updateMock(x, y, width, height, translationX, translationY)
+
+ return view
+ }
+
+ private fun View.updateMock(
+ x: Int = 0,
+ y: Int = 0,
+ width: Int = 10,
+ height: Int = 10,
+ translationX: Float = 0f,
+ translationY: Float = 0f
+ ) {
+ doAnswer {
+ val location = (it.arguments[0] as IntArray)
+ location[0] = x
+ location[1] = y
+ Unit
+ }.`when`(this).getLocationOnScreen(any())
+
+ whenever(this.width).thenReturn(width)
+ whenever(this.height).thenReturn(height)
+
+ this.apply {
setTranslationX(translationX)
setTranslationY(translationY)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
new file mode 100644
index 000000000000..096efad50615
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DisableFlagsLoggerTest.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertThrows
+import org.junit.Test
+
+@SmallTest
+class DisableFlagsLoggerTest : SysuiTestCase() {
+ private val disable1Flags = listOf(
+ DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'),
+ DisableFlagsLogger.DisableFlag(0b010, 'B', 'b'),
+ DisableFlagsLogger.DisableFlag(0b001, 'C', 'c'),
+ )
+ private val disable2Flags = listOf(
+ DisableFlagsLogger.DisableFlag(0b10, 'M', 'm'),
+ DisableFlagsLogger.DisableFlag(0b01, 'N', 'n'),
+ )
+
+ private val disableFlagsLogger = DisableFlagsLogger(disable1Flags, disable2Flags)
+
+ @Test
+ fun getDisableFlagsString_oldAndNewSame_statesLoggedButDiffsNotLogged() {
+ val state = DisableFlagsLogger.DisableState(
+ 0b111, // ABC
+ 0b01 // mN
+ )
+
+ val result = disableFlagsLogger.getDisableFlagsString(state, state)
+
+ assertThat(result).contains("Old: ABC.mN")
+ assertThat(result).contains("New: ABC.mN")
+ assertThat(result).doesNotContain("(")
+ assertThat(result).doesNotContain(")")
+ }
+
+ @Test
+ fun getDisableFlagsString_oldAndNewDifferent_statesAndDiffLogged() {
+ val result = disableFlagsLogger.getDisableFlagsString(
+ DisableFlagsLogger.DisableState(
+ 0b111, // ABC
+ 0b01, // mN
+ ),
+ DisableFlagsLogger.DisableState(
+ 0b001, // abC
+ 0b10 // Mn
+ )
+ )
+
+ assertThat(result).contains("Old: ABC.mN")
+ assertThat(result).contains("New: abC.Mn")
+ assertThat(result).contains("(ab.Mn)")
+ }
+
+ @Test
+ fun getDisableFlagsString_onlyDisable2Different_diffLoggedCorrectly() {
+ val result = disableFlagsLogger.getDisableFlagsString(
+ DisableFlagsLogger.DisableState(
+ 0b001, // abC
+ 0b01, // mN
+ ),
+ DisableFlagsLogger.DisableState(
+ 0b001, // abC
+ 0b00 // mn
+ )
+ )
+
+ assertThat(result).contains("(.n)")
+ }
+
+ @Test
+ fun getDisableFlagsString_nullLocalModification_localModNotLogged() {
+ val result = disableFlagsLogger.getDisableFlagsString(
+ DisableFlagsLogger.DisableState(0, 0),
+ DisableFlagsLogger.DisableState(1, 1),
+ newAfterLocalModification = null
+ )
+
+ assertThat(result).doesNotContain("local modification")
+ }
+
+ @Test
+ fun getDisableFlagsString_newAfterLocalModificationSameAsNew_localModNotLogged() {
+ val newState = DisableFlagsLogger.DisableState(
+ 0b001, // abC
+ 0b10 // mn
+ )
+
+ val result = disableFlagsLogger.getDisableFlagsString(
+ DisableFlagsLogger.DisableState(0, 0), newState, newState
+ )
+
+ assertThat(result).doesNotContain("local modification")
+ }
+
+ @Test
+ fun getDisableFlagsString_newAfterLocalModificationDifferent_localModAndDiffLogged() {
+ val result = disableFlagsLogger.getDisableFlagsString(
+ old = DisableFlagsLogger.DisableState(0, 0),
+ new = DisableFlagsLogger.DisableState(
+ 0b000, // abc
+ 0b00 // mn
+ ),
+ newAfterLocalModification = DisableFlagsLogger.DisableState(
+ 0b100, // Abc
+ 0b10 // Mn
+ )
+ )
+
+ assertThat(result).contains("local modification: Abc.Mn (A.M)")
+ }
+
+ @Test
+ fun constructor_defaultDisableFlags_noException() {
+ // Just creating the logger with the default params will trigger the exception if there
+ // is one.
+ DisableFlagsLogger()
+ }
+
+ @Test
+ fun constructor_disable1_FlagIsSetSymbolNotUnique_exception() {
+ assertThrows(IllegalArgumentException::class.java) {
+ DisableFlagsLogger(
+ disable1FlagsList = listOf(
+ DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'),
+ DisableFlagsLogger.DisableFlag(0b010, 'A', 'b'),
+ ),
+ listOf()
+ )
+ }
+ }
+
+ @Test
+ fun constructor_disable1_FlagNotSetSymbolNotUnique_exception() {
+ assertThrows(IllegalArgumentException::class.java) {
+ DisableFlagsLogger(
+ disable1FlagsList = listOf(
+ DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'),
+ DisableFlagsLogger.DisableFlag(0b010, 'B', 'a'),
+ ),
+ listOf()
+ )
+ }
+ }
+
+ @Test
+ fun constructor_disable2_FlagIsSetSymbolNotUnique_exception() {
+ assertThrows(IllegalArgumentException::class.java) {
+ DisableFlagsLogger(
+ disable1FlagsList = listOf(),
+ disable2FlagsList = listOf(
+ DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'),
+ DisableFlagsLogger.DisableFlag(0b010, 'A', 'b'),
+ ),
+ )
+ }
+ }
+
+ @Test
+ fun constructor_disable2_FlagNotSetSymbolNotUnique_exception() {
+ assertThrows(IllegalArgumentException::class.java) {
+ DisableFlagsLogger(
+ disable1FlagsList = listOf(),
+ disable2FlagsList = listOf(
+ DisableFlagsLogger.DisableFlag(0b100, 'A', 'a'),
+ DisableFlagsLogger.DisableFlag(0b010, 'B', 'a'),
+ ),
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 60f0b68acac3..4276f7ce7b12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -274,6 +274,26 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
}
@Test
+ public void onBiometricAuthenticated_onLockScreen() {
+ // GIVEN not dozing
+ when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
+
+ // WHEN we want to unlock collapse
+ mBiometricUnlockController.startWakeAndUnlock(
+ BiometricUnlockController.MODE_UNLOCK_COLLAPSING);
+
+ // THEN we collpase the panels and notify authenticated
+ verify(mShadeController).animateCollapsePanels(
+ /* flags */ anyInt(),
+ /* force */ eq(true),
+ /* delayed */ eq(false),
+ /* speedUpFactor */ anyFloat()
+ );
+ verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(
+ /* strongAuth */ eq(false));
+ }
+
+ @Test
public void onBiometricAuthenticated_whenFace_noBypass_encrypted_doNothing() {
reset(mUpdateMonitor);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt
new file mode 100644
index 000000000000..f3136c7be967
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentLoggerTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.statusbar.DisableFlagsLogger
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.Mockito.mock
+import java.io.PrintWriter
+import java.io.StringWriter
+
+@SmallTest
+class CollapsedStatusBarFragmentLoggerTest : SysuiTestCase() {
+
+ private val buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
+ .create("buffer", 10)
+ private val disableFlagsLogger = DisableFlagsLogger(
+ listOf(DisableFlagsLogger.DisableFlag(0b001, 'A', 'a')),
+ listOf(DisableFlagsLogger.DisableFlag(0b001, 'B', 'b'))
+ )
+ private val logger = CollapsedStatusBarFragmentLogger(buffer, disableFlagsLogger)
+
+ @Test
+ fun logToBuffer_bufferHasStates() {
+ val state = DisableFlagsLogger.DisableState(0, 1)
+
+ logger.logDisableFlagChange(state, state)
+
+ val stringWriter = StringWriter()
+ buffer.dump(PrintWriter(stringWriter), tailLength = 0)
+ val actualString = stringWriter.toString()
+ val expectedLogString = disableFlagsLogger.getDisableFlagsString(state, state)
+
+ assertThat(actualString).contains(expectedLogString)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 5f4670045ad6..b08dbee687e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -39,8 +39,11 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.LogcatEchoTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
@@ -263,6 +266,10 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mNetworkController,
mStatusBarStateController,
mCommandQueue,
+ new CollapsedStatusBarFragmentLogger(
+ new LogBuffer("TEST", 1, 1, mock(LogcatEchoTracker.class)),
+ new DisableFlagsLogger()
+ ),
mOperatorNameViewControllerFactory);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index f34f21bde803..d098e1a0b8a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -16,42 +16,80 @@
package com.android.systemui.statusbar.phone;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.AdditionalAnswers.returnsFirstArg;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.doze.util.BurnInHelperKt;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
private static final int SCREEN_HEIGHT = 2000;
- private static final int EMPTY_MARGIN = 0;
private static final int EMPTY_HEIGHT = 0;
private static final float ZERO_DRAG = 0.f;
private static final float OPAQUE = 1.f;
private static final float TRANSPARENT = 0.f;
+
+ @Mock
+ private Resources mResources;
+
private KeyguardClockPositionAlgorithm mClockPositionAlgorithm;
private KeyguardClockPositionAlgorithm.Result mClockPosition;
+
+ private MockitoSession mStaticMockSession;
+ private int mNotificationStackHeight;
+
private float mPanelExpansion;
+ private int mKeyguardStatusBarHeaderHeight;
private int mKeyguardStatusHeight;
private float mDark;
private float mQsExpansion;
- private int mCutoutTopInsetPx = 0;
+ private int mCutoutTopInset = 0;
private boolean mIsSplitShade = false;
+ private float mUdfpsTop = -1;
+ private float mClockBottom = SCREEN_HEIGHT / 2;
+ private boolean mClockTopAligned;
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mStaticMockSession = mockitoSession()
+ .mockStatic(BurnInHelperKt.class)
+ .startMocking();
+
mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm();
+ when(mResources.getDimensionPixelSize(anyInt())).thenReturn(0);
+ mClockPositionAlgorithm.loadDimens(mResources);
+
mClockPosition = new KeyguardClockPositionAlgorithm.Result();
}
+ @After
+ public void tearDown() {
+ mStaticMockSession.finishMocking();
+ }
+
@Test
public void clockPositionTopOfScreenOnAOD() {
// GIVEN on AOD and clock has 0 height
@@ -71,7 +109,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
// GIVEN on AOD and clock has 0 height
givenAOD();
mKeyguardStatusHeight = EMPTY_HEIGHT;
- mCutoutTopInsetPx = 300;
+ mCutoutTopInset = 300;
// WHEN the clock position algorithm is run
positionClock();
// THEN the clock Y position is below the cutout
@@ -271,6 +309,155 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
assertThat(mClockPosition.clockAlpha).isEqualTo(TRANSPARENT);
}
+ @Test
+ public void clockPositionMinimizesBurnInMovementToAvoidUdfpsOnAOD() {
+ // GIVEN a center aligned clock
+ mClockTopAligned = false;
+
+ // GIVEN the clock + udfps are 100px apart
+ mClockBottom = SCREEN_HEIGHT - 500;
+ mUdfpsTop = SCREEN_HEIGHT - 400;
+
+ // GIVEN it's AOD and the burn-in y value is 200
+ givenAOD();
+ givenMaxBurnInOffset(200);
+
+ // WHEN the clock position algorithm is run with the highest burn in offset
+ givenHighestBurnInOffset();
+ positionClock();
+
+ // THEN the worst-case clock Y position is shifted only by 100 (not the full 200),
+ // so that it's at the same location as mUdfpsTop
+ assertThat(mClockPosition.clockY).isEqualTo(100);
+
+ // WHEN the clock position algorithm is run with the lowest burn in offset
+ givenLowestBurnInOffset();
+ positionClock();
+
+ // THEN lowest case starts at 0
+ assertThat(mClockPosition.clockY).isEqualTo(0);
+ }
+
+ @Test
+ public void clockPositionShiftsToAvoidUdfpsOnAOD_usesSpaceAboveClock() {
+ // GIVEN a center aligned clock
+ mClockTopAligned = false;
+
+ // GIVEN there's space at the top of the screen on LS (that's available to be used for
+ // burn-in on AOD)
+ mKeyguardStatusBarHeaderHeight = 150;
+
+ // GIVEN the bottom of the clock is beyond the top of UDFPS
+ mClockBottom = SCREEN_HEIGHT - 300;
+ mUdfpsTop = SCREEN_HEIGHT - 400;
+
+ // GIVEN it's AOD and the burn-in y value is 200
+ givenAOD();
+ givenMaxBurnInOffset(200);
+
+ // WHEN the clock position algorithm is run with the highest burn in offset
+ givenHighestBurnInOffset();
+ positionClock();
+
+ // THEN the algo should shift the clock up and use the area above the clock for
+ // burn-in since the burn in offset > space above clock
+ assertThat(mClockPosition.clockY).isEqualTo(mKeyguardStatusBarHeaderHeight);
+
+ // WHEN the clock position algorithm is run with the lowest burn in offset
+ givenLowestBurnInOffset();
+ positionClock();
+
+ // THEN lowest case starts at mCutoutTopInset (0 in this case)
+ assertThat(mClockPosition.clockY).isEqualTo(mCutoutTopInset);
+ }
+
+ @Test
+ public void clockPositionShiftsToAvoidUdfpsOnAOD_usesMaxBurnInOffset() {
+ // GIVEN a center aligned clock
+ mClockTopAligned = false;
+
+ // GIVEN there's 200px space at the top of the screen on LS (that's available to be used for
+ // burn-in on AOD) but 50px are taken up by the cutout
+ mKeyguardStatusBarHeaderHeight = 200;
+ mCutoutTopInset = 50;
+
+ // GIVEN the bottom of the clock is beyond the top of UDFPS
+ mClockBottom = SCREEN_HEIGHT - 300;
+ mUdfpsTop = SCREEN_HEIGHT - 400;
+
+ // GIVEN it's AOD and the burn-in y value is only 25px (less than space above clock)
+ givenAOD();
+ int maxYBurnInOffset = 25;
+ givenMaxBurnInOffset(maxYBurnInOffset);
+
+ // WHEN the clock position algorithm is run with the highest burn in offset
+ givenHighestBurnInOffset();
+ positionClock();
+
+ // THEN the algo should shift the clock up and use the area above the clock for
+ // burn-in
+ assertThat(mClockPosition.clockY).isEqualTo(mKeyguardStatusBarHeaderHeight);
+
+ // WHEN the clock position algorithm is run with the lowest burn in offset
+ givenLowestBurnInOffset();
+ positionClock();
+
+ // THEN lowest case starts above mKeyguardStatusBarHeaderHeight
+ assertThat(mClockPosition.clockY).isEqualTo(
+ mKeyguardStatusBarHeaderHeight - 2 * maxYBurnInOffset);
+ }
+
+ @Test
+ public void clockPositionShiftsToMaximizeUdfpsBurnInMovement() {
+ // GIVEN a center aligned clock
+ mClockTopAligned = false;
+
+ // GIVEN there's 200px space at the top of the screen on LS (that's available to be used for
+ // burn-in on AOD) but 50px are taken up by the cutout
+ mKeyguardStatusBarHeaderHeight = 200;
+ mCutoutTopInset = 50;
+ int upperSpaceAvailable = mKeyguardStatusBarHeaderHeight - mCutoutTopInset;
+
+ // GIVEN the bottom of the clock and the top of UDFPS are 100px apart
+ mClockBottom = SCREEN_HEIGHT - 500;
+ mUdfpsTop = SCREEN_HEIGHT - 400;
+ float lowerSpaceAvailable = mUdfpsTop - mClockBottom;
+
+ // GIVEN it's AOD and the burn-in y value is 200
+ givenAOD();
+ givenMaxBurnInOffset(200);
+
+ // WHEN the clock position algorithm is run with the highest burn in offset
+ givenHighestBurnInOffset();
+ positionClock();
+
+ // THEN the algo should shift the clock up and use both the area above
+ // the clock and below the clock (vertically centered in its allowed area)
+ assertThat(mClockPosition.clockY).isEqualTo(
+ (int) (mCutoutTopInset + upperSpaceAvailable + lowerSpaceAvailable));
+
+ // WHEN the clock position algorithm is run with the lowest burn in offset
+ givenLowestBurnInOffset();
+ positionClock();
+
+ // THEN lowest case starts at mCutoutTopInset
+ assertThat(mClockPosition.clockY).isEqualTo(mCutoutTopInset);
+ }
+
+ private void givenHighestBurnInOffset() {
+ when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).then(returnsFirstArg());
+ }
+
+ private void givenLowestBurnInOffset() {
+ when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(0);
+ }
+
+ private void givenMaxBurnInOffset(int offset) {
+ when(mResources.getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y_clock))
+ .thenReturn(offset);
+ mClockPositionAlgorithm.loadDimens(mResources);
+ }
+
private void givenAOD() {
mPanelExpansion = 1.f;
mDark = 1.f;
@@ -281,12 +468,28 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
mDark = 0.f;
}
+ /**
+ * Setup and run the clock position algorithm.
+ *
+ * mClockPosition.clockY will contain the top y-coordinate for the clock position
+ */
private void positionClock() {
- mClockPositionAlgorithm.setup(EMPTY_MARGIN, mPanelExpansion, mKeyguardStatusHeight,
- 0 /* userSwitchHeight */, 0 /* userSwitchPreferredY */,
- mDark, ZERO_DRAG, false /* bypassEnabled */,
- 0 /* unlockedStackScrollerPadding */, mQsExpansion,
- mCutoutTopInsetPx, mIsSplitShade);
+ mClockPositionAlgorithm.setup(
+ mKeyguardStatusBarHeaderHeight,
+ mPanelExpansion,
+ mKeyguardStatusHeight,
+ 0 /* userSwitchHeight */,
+ 0 /* userSwitchPreferredY */,
+ mDark,
+ ZERO_DRAG,
+ false /* bypassEnabled */,
+ 0 /* unlockedStackScrollerPadding */,
+ mQsExpansion,
+ mCutoutTopInset,
+ mIsSplitShade,
+ mUdfpsTop,
+ mClockBottom,
+ mClockTopAligned);
mClockPositionAlgorithm.run(mClockPosition);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 3d887dd92be2..c62d73c98354 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -32,6 +32,7 @@ import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
+import com.android.keyguard.LockIconViewController;
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
@@ -96,6 +97,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ @Mock private LockIconViewController mLockIconViewController;
@Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
mInteractionEventHandlerCaptor;
@@ -141,7 +143,8 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
mNotificationPanelViewController,
mStatusBarWindowView,
mNotificationStackScrollLayoutController,
- mStatusBarKeyguardViewManager);
+ mStatusBarKeyguardViewManager,
+ mLockIconViewController);
mController.setupExpandedStatusBar();
mController.setService(mStatusBar, mNotificationShadeWindowController);
mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index d63730d596d0..52a5e064f984 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -16,21 +16,39 @@
package com.android.systemui.statusbar.phone
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.FrameLayout
import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
class PhoneStatusBarViewControllerTest : SysuiTestCase() {
+ private val stateChangeListener = TestStateChangedListener()
+
@Mock
private lateinit var commandQueue: CommandQueue
+ @Mock
+ private lateinit var panelViewController: PanelViewController
+ @Mock
+ private lateinit var panelView: ViewGroup
+ @Mock
+ private lateinit var scrimController: ScrimController
+
+ @Mock
+ private lateinit var moveFromCenterAnimation: StatusBarMoveFromCenterAnimationController
private lateinit var view: PhoneStatusBarView
private lateinit var controller: PhoneStatusBarViewController
@@ -38,8 +56,23 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- view = PhoneStatusBarView(mContext, null)
- controller = PhoneStatusBarViewController(view, commandQueue)
+ `when`(panelViewController.view).thenReturn(panelView)
+
+ // create the view on main thread as it requires main looper
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ val parent = FrameLayout(mContext) // add parent to keep layout params
+ view = LayoutInflater.from(mContext)
+ .inflate(R.layout.status_bar, parent, false) as PhoneStatusBarView
+ view.setPanel(panelViewController)
+ view.setScrimController(scrimController)
+ }
+
+ controller = PhoneStatusBarViewController(
+ view,
+ commandQueue,
+ null,
+ stateChangeListener
+ )
}
@Test
@@ -56,4 +89,32 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
assertThat(providerUsed).isTrue()
}
+
+ @Test
+ fun constructor_moveFromCenterAnimationIsNotNull_moveFromCenterAnimationInitialized() {
+ controller = PhoneStatusBarViewController(
+ view, commandQueue, moveFromCenterAnimation, stateChangeListener
+ )
+
+ verify(moveFromCenterAnimation).init(any(), any())
+ }
+
+ @Test
+ fun constructor_setsExpansionStateChangedListenerOnView() {
+ assertThat(stateChangeListener.stateChangeCalled).isFalse()
+
+ // If the constructor correctly set the listener, then it should be used when
+ // [PhoneStatusBarView.panelExpansionChanged] is called.
+ view.panelExpansionChanged(0f, false)
+
+ assertThat(stateChangeListener.stateChangeCalled).isTrue()
+ }
+
+ private class TestStateChangedListener : PhoneStatusBarView.PanelExpansionStateChangedListener {
+ var stateChangeCalled: Boolean = false
+
+ override fun onPanelExpansionStateChanged() {
+ stateChangeCalled = true
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index 49ab6ebbde93..aee9f12c3844 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -16,20 +16,38 @@
package com.android.systemui.statusbar.phone
+import android.view.ViewGroup
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.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
@SmallTest
class PhoneStatusBarViewTest : SysuiTestCase() {
+ @Mock
+ private lateinit var panelViewController: PanelViewController
+ @Mock
+ private lateinit var panelView: ViewGroup
+ @Mock
+ private lateinit var scrimController: ScrimController
+
private lateinit var view: PhoneStatusBarView
@Before
fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ // TODO(b/197137564): Setting up a panel view and its controller feels unnecessary when
+ // testing just [PhoneStatusBarView].
+ `when`(panelViewController.view).thenReturn(panelView)
+
view = PhoneStatusBarView(mContext, null)
+ view.setPanel(panelViewController)
+ view.setScrimController(scrimController)
}
@Test
@@ -51,4 +69,48 @@ class PhoneStatusBarViewTest : SysuiTestCase() {
view.panelEnabled()
// No assert needed, just testing no crash
}
+
+ @Test
+ fun panelExpansionChanged_fracZero_stateChangeListenerNotified() {
+ val listener = TestStateChangedListener()
+ view.setPanelExpansionStateChangedListener(listener)
+
+ view.panelExpansionChanged(0f, false)
+
+ assertThat(listener.stateChangeCalled).isTrue()
+ }
+
+ @Test
+ fun panelExpansionChanged_fracOne_stateChangeListenerNotified() {
+ val listener = TestStateChangedListener()
+ view.setPanelExpansionStateChangedListener(listener)
+
+ view.panelExpansionChanged(1f, false)
+
+ assertThat(listener.stateChangeCalled).isTrue()
+ }
+
+ @Test
+ fun panelExpansionChanged_fracHalf_stateChangeListenerNotNotified() {
+ val listener = TestStateChangedListener()
+ view.setPanelExpansionStateChangedListener(listener)
+
+ view.panelExpansionChanged(0.5f, false)
+
+ assertThat(listener.stateChangeCalled).isFalse()
+ }
+
+ @Test
+ fun panelExpansionChanged_noStateChangeListener_noCrash() {
+ view.panelExpansionChanged(1f, false)
+ // No assert needed, just testing no crash
+ }
+
+ private class TestStateChangedListener : PhoneStatusBarView.PanelExpansionStateChangedListener {
+ var stateChangeCalled: Boolean = false
+
+ override fun onPanelExpansionStateChanged() {
+ stateChangeCalled = true
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
index 52538c7f5496..8555306bae04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
@@ -38,6 +38,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -112,6 +113,7 @@ public class StatusBarCommandQueueCallbacksTest extends SysuiTestCase {
mVibratorHelper,
Optional.of(mVibrator),
mLightBarController,
+ new DisableFlagsLogger(),
DEFAULT_DISPLAY);
when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index b23414bacf10..751bc815bdd6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -233,6 +233,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private ViewMediatorCallback mKeyguardVieMediatorCallback;
@Mock private VolumeComponent mVolumeComponent;
@Mock private CommandQueue mCommandQueue;
+ @Mock private CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
@Mock private StatusBarComponent.Factory mStatusBarComponentFactory;
@Mock private StatusBarComponent mStatusBarComponent;
@Mock private PluginManager mPluginManager;
@@ -254,6 +255,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
@Mock private UnfoldTransitionConfig mUnfoldTransitionConfig;
@Mock private Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimationLazy;
+ @Mock private Lazy<StatusBarMoveFromCenterAnimationController> mMoveFromCenterAnimationLazy;
@Mock private OngoingCallController mOngoingCallController;
@Mock private SystemStatusAnimationScheduler mAnimationScheduler;
@Mock private StatusBarLocationPublisher mLocationPublisher;
@@ -403,6 +405,7 @@ public class StatusBarTest extends SysuiTestCase {
mDozeScrimController,
mVolumeComponent,
mCommandQueue,
+ mCollapsedStatusBarFragmentLogger,
mStatusBarComponentFactory,
mPluginManager,
Optional.of(mLegacySplitScreen),
@@ -428,6 +431,7 @@ public class StatusBarTest extends SysuiTestCase {
mBrightnessSliderFactory,
mUnfoldTransitionConfig,
mUnfoldLightRevealOverlayAnimationLazy,
+ mMoveFromCenterAnimationLazy,
mOngoingCallController,
mAnimationScheduler,
mLocationPublisher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index cd911cc533f7..4c282bfaed93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -167,7 +167,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase {
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
.isEqualTo(new OverlayIdentifier("ffff0000"));
assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
- .isEqualTo(new OverlayIdentifier("ff0000ff"));
+ .isEqualTo(new OverlayIdentifier("ffff0000"));
// Should not ask again if changed to same value
mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 30de4b416410..4748a86b79c6 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -566,7 +566,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
return PendingIntent.getActivityAsUser(getContext(),
0 /* request code */,
NotificationAccessConfirmationActivityContract.launcherIntent(
- userId, component, packageTitle),
+ getContext(), userId, component, packageTitle),
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT
| PendingIntent.FLAG_CANCEL_CURRENT,
null /* options */,
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 10071309bc0d..81627a05c9a4 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -1167,11 +1167,16 @@ final class UiModeManagerService extends SystemService {
}
private boolean doesPackageHaveCallingUid(@NonNull String packageName) {
+ int callingUid = mInjector.getCallingUid();
+ int callingUserId = UserHandle.getUserId(callingUid);
+ final long ident = Binder.clearCallingIdentity();
try {
return getContext().getPackageManager().getPackageUidAsUser(packageName,
- UserHandle.getCallingUserId()) == mInjector.getCallingUid();
+ callingUserId) == callingUid;
} catch (PackageManager.NameNotFoundException e) {
return false;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index f32aa2295cb8..9da6d528a967 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -157,6 +157,9 @@ public final class CachedAppOptimizer {
static final int SYNC_RECEIVED_WHILE_FROZEN = 1;
static final int ASYNC_RECEIVED_WHILE_FROZEN = 2;
+ // Bitfield values for sync transactions received by frozen binder threads
+ static final int TXNS_PENDING_WHILE_FROZEN = 4;
+
/**
* This thread must be moved to the system background cpuset.
* If that doesn't happen, it's probably going to draw a lot of power.
@@ -611,8 +614,9 @@ public final class CachedAppOptimizer {
* binder for the specificed pid.
*
* @throws RuntimeException in case a flush/freeze operation could not complete successfully.
+ * @return 0 if success, or -EAGAIN indicating there's pending transaction.
*/
- private static native void freezeBinder(int pid, boolean freeze);
+ private static native int freezeBinder(int pid, boolean freeze);
/**
* Retrieves binder freeze info about a process.
@@ -948,7 +952,7 @@ public final class CachedAppOptimizer {
int freezeInfo = getBinderFreezeInfo(pid);
if ((freezeInfo & SYNC_RECEIVED_WHILE_FROZEN) != 0) {
- Slog.d(TAG_AM, "pid " + pid + " " + app.processName + " "
+ Slog.d(TAG_AM, "pid " + pid + " " + app.processName
+ " received sync transactions while frozen, killing");
app.killLocked("Sync transaction while in frozen state",
ApplicationExitInfo.REASON_OTHER,
@@ -956,8 +960,8 @@ public final class CachedAppOptimizer {
processKilled = true;
}
- if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0) {
- Slog.d(TAG_AM, "pid " + pid + " " + app.processName + " "
+ if ((freezeInfo & ASYNC_RECEIVED_WHILE_FROZEN) != 0 && DEBUG_FREEZER) {
+ Slog.d(TAG_AM, "pid " + pid + " " + app.processName
+ " received async transactions while frozen");
}
} catch (Exception e) {
@@ -1292,7 +1296,9 @@ public final class CachedAppOptimizer {
public void handleMessage(Message msg) {
switch (msg.what) {
case SET_FROZEN_PROCESS_MSG:
- freezeProcess((ProcessRecord) msg.obj);
+ synchronized (mAm) {
+ freezeProcess((ProcessRecord) msg.obj);
+ }
break;
case REPORT_UNFREEZE_MSG:
int pid = msg.arg1;
@@ -1306,6 +1312,15 @@ public final class CachedAppOptimizer {
}
}
+ @GuardedBy({"mAm", "mProcLock"})
+ private void rescheduleFreeze(final ProcessRecord proc, final String reason) {
+ Slog.d(TAG_AM, "Reschedule freeze for process " + proc.getPid()
+ + " " + proc.processName + " (" + reason + ")");
+ unfreezeAppLSP(proc);
+ freezeAppAsyncLSP(proc);
+ }
+
+ @GuardedBy({"mAm"})
private void freezeProcess(final ProcessRecord proc) {
int pid = proc.getPid(); // Unlocked intentionally
final String name = proc.processName;
@@ -1355,10 +1370,15 @@ public final class CachedAppOptimizer {
return;
}
+ Slog.d(TAG_AM, "freezing " + pid + " " + name);
+
// Freeze binder interface before the process, to flush any
// transactions that might be pending.
try {
- freezeBinder(pid, true);
+ if (freezeBinder(pid, true) != 0) {
+ rescheduleFreeze(proc, "outstanding txns");
+ return;
+ }
} catch (RuntimeException e) {
Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
mFreezeHandler.post(() -> {
@@ -1404,24 +1424,36 @@ public final class CachedAppOptimizer {
try {
// post-check to prevent races
+ int freezeInfo = getBinderFreezeInfo(pid);
+
+ if ((freezeInfo & TXNS_PENDING_WHILE_FROZEN) != 0) {
+ synchronized (mProcLock) {
+ rescheduleFreeze(proc, "new pending txns");
+ }
+ return;
+ }
+ } catch (RuntimeException e) {
+ Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
+ mFreezeHandler.post(() -> {
+ synchronized (mAm) {
+ proc.killLocked("Unable to freeze binder interface",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
+ }
+ });
+ }
+
+ try {
+ // post-check to prevent races
if (mProcLocksReader.hasFileLocks(pid)) {
if (DEBUG_FREEZER) {
Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, reverting freeze");
}
-
- synchronized (mAm) {
- synchronized (mProcLock) {
- unfreezeAppLSP(proc);
- }
- }
+ unfreezeAppLSP(proc);
}
} catch (Exception e) {
Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
- synchronized (mAm) {
- synchronized (mProcLock) {
- unfreezeAppLSP(proc);
- }
- }
+ unfreezeAppLSP(proc);
}
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index e022e977e02f..2f20efbf5730 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -84,6 +84,7 @@ public class SettingsToPropertiesMapper {
DeviceConfig.NAMESPACE_CONNECTIVITY,
DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
+ DeviceConfig.NAMESPACE_LMKD_NATIVE,
DeviceConfig.NAMESPACE_MEDIA_NATIVE,
DeviceConfig.NAMESPACE_NETD_NATIVE,
DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b9b90c0f64ea..5d0bad23f024 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -94,6 +94,8 @@ import android.media.IPlaybackConfigDispatcher;
import android.media.IRecordingConfigDispatcher;
import android.media.IRingtonePlayer;
import android.media.ISpatializerCallback;
+import android.media.ISpatializerHeadToSoundStagePoseCallback;
+import android.media.ISpatializerHeadTrackingModeCallback;
import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.IVolumeController;
import android.media.MediaMetrics;
@@ -200,7 +202,8 @@ import java.util.stream.Collectors;
*/
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
- AccessibilityManager.AccessibilityServicesStateChangeListener {
+ AccessibilityManager.AccessibilityServicesStateChangeListener,
+ AudioSystemAdapter.OnRoutingUpdatedListener {
private static final String TAG = "AS.AudioService";
@@ -314,12 +317,14 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_SET_A2DP_DEV_CONNECTION_STATE = 38;
private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39;
private static final int MSG_DISPATCH_AUDIO_MODE = 40;
+ private static final int MSG_ROUTING_UPDATED = 41;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
private static final int MSG_DISABLE_AUDIO_FOR_UID = 100;
private static final int MSG_INIT_STREAMS_VOLUMES = 101;
+ private static final int MSG_INIT_SPATIALIZER = 102;
// end of messages handled under wakelock
// retry delay in case of failure to indicate system ready to AudioFlinger
@@ -869,6 +874,8 @@ public class AudioService extends IAudioService.Stub
mSfxHelper = new SoundEffectsHelper(mContext);
+ mSpatializerHelper = new SpatializerHelper(this, mAudioSystem);
+
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
@@ -1033,6 +1040,9 @@ public class AudioService extends IAudioService.Stub
// done with service initialization, continue additional work in our Handler thread
queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_STREAMS_VOLUMES,
0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
+ queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_SPATIALIZER,
+ 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
+
}
/**
@@ -1222,6 +1232,22 @@ public class AudioService extends IAudioService.Stub
updateVibratorInfos();
}
+ //-----------------------------------------------------------------
+ // routing monitoring from AudioSystemAdapter
+ @Override
+ public void onRoutingUpdatedFromNative() {
+ sendMsg(mAudioHandler,
+ MSG_ROUTING_UPDATED,
+ SENDMSG_REPLACE, 0, 0, null,
+ /*delay*/ 0);
+ }
+
+ void monitorRoutingChanges(boolean enabled) {
+ mAudioSystem.setRoutingListener(enabled ? this : null);
+ }
+
+
+ //-----------------------------------------------------------------
RoleObserver mRoleObserver;
class RoleObserver implements OnRoleHoldersChangedListener {
@@ -1406,6 +1432,9 @@ public class AudioService extends IAudioService.Stub
}
}
+ // TODO check property if feature enabled
+ mSpatializerHelper.reset(/* featureEnabled */ SPATIALIZER_FEATURE_ENABLED_DEFAULT);
+
onIndicateSystemReady();
// indicate the end of reconfiguration phase to audio HAL
AudioSystem.setParameters("restarting=false");
@@ -7539,6 +7568,13 @@ public class AudioService extends IAudioService.Stub
mAudioEventWakeLock.release();
break;
+ case MSG_INIT_SPATIALIZER:
+ mSpatializerHelper.init();
+ // TODO read property to see if enabled
+ mSpatializerHelper.setFeatureEnabled(SPATIALIZER_FEATURE_ENABLED_DEFAULT);
+ mAudioEventWakeLock.release();
+ break;
+
case MSG_CHECK_MUSIC_ACTIVE:
onCheckMusicActive((String) msg.obj);
break;
@@ -7671,6 +7707,10 @@ public class AudioService extends IAudioService.Stub
case MSG_DISPATCH_AUDIO_MODE:
dispatchMode(msg.arg1);
break;
+
+ case MSG_ROUTING_UPDATED:
+ mSpatializerHelper.onRoutingUpdated();
+ break;
}
}
}
@@ -8239,7 +8279,8 @@ public class AudioService extends IAudioService.Stub
}
//==========================================================================================
- private final SpatializerHelper mSpatializerHelper = new SpatializerHelper();
+ private final @NonNull SpatializerHelper mSpatializerHelper;
+ private static final boolean SPATIALIZER_FEATURE_ENABLED_DEFAULT = false;
private void enforceModifyDefaultAudioEffectsPermission() {
if (mContext.checkCallingOrSelfPermission(
@@ -8249,9 +8290,12 @@ public class AudioService extends IAudioService.Stub
}
}
- /** @see AudioManager#getSpatializerImmersiveAudioLevel() */
+ /**
+ * Returns the immersive audio level that the platform is capable of
+ * @see Spatializer#getImmersiveAudioLevel()
+ */
public int getSpatializerImmersiveAudioLevel() {
- return mSpatializerHelper.getImmersiveAudioLevel();
+ return mSpatializerHelper.getCapableImmersiveAudioLevel();
}
/** @see Spatializer#isEnabled() */
@@ -8267,7 +8311,7 @@ public class AudioService extends IAudioService.Stub
/** @see Spatializer#setSpatializerEnabled(boolean) */
public void setSpatializerEnabled(boolean enabled) {
enforceModifyDefaultAudioEffectsPermission();
- mSpatializerHelper.setEnabled(enabled);
+ mSpatializerHelper.setFeatureEnabled(enabled);
}
/** @see Spatializer#canBeSpatialized() */
@@ -8280,16 +8324,48 @@ public class AudioService extends IAudioService.Stub
/** @see Spatializer.SpatializerInfoDispatcherStub */
public void registerSpatializerCallback(
- @NonNull ISpatializerCallback dispatcher) {
- Objects.requireNonNull(dispatcher);
- mSpatializerHelper.registerStateCallback(dispatcher);
+ @NonNull ISpatializerCallback cb) {
+ Objects.requireNonNull(cb);
+ mSpatializerHelper.registerStateCallback(cb);
}
/** @see Spatializer.SpatializerInfoDispatcherStub */
public void unregisterSpatializerCallback(
- @NonNull ISpatializerCallback dispatcher) {
- Objects.requireNonNull(dispatcher);
- mSpatializerHelper.unregisterStateCallback(dispatcher);
+ @NonNull ISpatializerCallback cb) {
+ Objects.requireNonNull(cb);
+ mSpatializerHelper.unregisterStateCallback(cb);
+ }
+
+ /** @see Spatializer#SpatializerHeadTrackingDispatcherStub */
+ public void registerSpatializerHeadTrackingCallback(
+ @NonNull ISpatializerHeadTrackingModeCallback cb) {
+ enforceModifyDefaultAudioEffectsPermission();
+ Objects.requireNonNull(cb);
+ mSpatializerHelper.registerHeadTrackingModeCallback(cb);
+ }
+
+ /** @see Spatializer#SpatializerHeadTrackingDispatcherStub */
+ public void unregisterSpatializerHeadTrackingCallback(
+ @NonNull ISpatializerHeadTrackingModeCallback cb) {
+ enforceModifyDefaultAudioEffectsPermission();
+ Objects.requireNonNull(cb);
+ mSpatializerHelper.unregisterHeadTrackingModeCallback(cb);
+ }
+
+ /** @see Spatializer#setOnHeadToSoundstagePoseUpdatedListener */
+ public void registerHeadToSoundstagePoseCallback(
+ @NonNull ISpatializerHeadToSoundStagePoseCallback cb) {
+ enforceModifyDefaultAudioEffectsPermission();
+ Objects.requireNonNull(cb);
+ mSpatializerHelper.registerHeadToSoundstagePoseCallback(cb);
+ }
+
+ /** @see Spatializer#clearOnHeadToSoundstagePoseUpdatedListener */
+ public void unregisterHeadToSoundstagePoseCallback(
+ @NonNull ISpatializerHeadToSoundStagePoseCallback cb) {
+ enforceModifyDefaultAudioEffectsPermission();
+ Objects.requireNonNull(cb);
+ mSpatializerHelper.unregisterHeadToSoundstagePoseCallback(cb);
}
/** @see Spatializer#getSpatializerCompatibleAudioDevices() */
@@ -8312,6 +8388,51 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.removeCompatibleAudioDevice(ada);
}
+ /** @see Spatializer#getSupportedHeadTrackingModes() */
+ public int[] getSupportedHeadTrackingModes() {
+ enforceModifyDefaultAudioEffectsPermission();
+ return mSpatializerHelper.getSupportedHeadTrackingModes();
+ }
+
+ /** @see Spatializer#getHeadTrackingMode() */
+ public int getActualHeadTrackingMode() {
+ enforceModifyDefaultAudioEffectsPermission();
+ return mSpatializerHelper.getActualHeadTrackingMode();
+ }
+
+ /** @see Spatializer#getDesiredHeadTrackingMode() */
+ public int getDesiredHeadTrackingMode() {
+ enforceModifyDefaultAudioEffectsPermission();
+ return mSpatializerHelper.getDesiredHeadTrackingMode();
+ }
+
+ /** @see Spatializer#setGlobalTransform */
+ public void setSpatializerGlobalTransform(@NonNull float[] transform) {
+ enforceModifyDefaultAudioEffectsPermission();
+ Objects.requireNonNull(transform);
+ mSpatializerHelper.setGlobalTransform(transform);
+ }
+
+ /** @see Spatializer#recenterHeadTracker() */
+ public void recenterHeadTracker() {
+ enforceModifyDefaultAudioEffectsPermission();
+ mSpatializerHelper.recenterHeadTracker();
+ }
+
+ /** @see Spatializer#setDesiredHeadTrackingMode */
+ public void setDesiredHeadTrackingMode(@Spatializer.HeadTrackingModeSet int mode) {
+ enforceModifyDefaultAudioEffectsPermission();
+ switch(mode) {
+ case Spatializer.HEAD_TRACKING_MODE_DISABLED:
+ case Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD:
+ case Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE:
+ break;
+ default:
+ return;
+ }
+ mSpatializerHelper.setDesiredHeadTrackingMode(mode);
+ }
+
//==========================================================================================
private boolean readCameraSoundForced() {
return SystemProperties.getBoolean("audio.camerasound.force", false) ||
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 6d567807f357..ac212eee21e6 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -17,6 +17,7 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioSystem;
@@ -24,6 +25,8 @@ import android.media.audiopolicy.AudioMix;
import android.os.SystemClock;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -59,6 +62,9 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
private ConcurrentHashMap<AudioAttributes, ArrayList<AudioDeviceAttributes>>
mDevicesForAttrCache;
private int[] mMethodCacheHit;
+ private static final Object sRoutingListenerLock = new Object();
+ @GuardedBy("sRoutingListenerLock")
+ private static @Nullable OnRoutingUpdatedListener sRoutingListener;
/**
* should be false except when trying to debug caching errors. When true, the value retrieved
@@ -76,6 +82,23 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
Log.d(TAG, "---- onRoutingUpdated (from native) ----------");
}
invalidateRoutingCache();
+ final OnRoutingUpdatedListener listener;
+ synchronized (sRoutingListenerLock) {
+ listener = sRoutingListener;
+ }
+ if (listener != null) {
+ listener.onRoutingUpdatedFromNative();
+ }
+ }
+
+ interface OnRoutingUpdatedListener {
+ void onRoutingUpdatedFromNative();
+ }
+
+ static void setRoutingListener(@Nullable OnRoutingUpdatedListener listener) {
+ synchronized (sRoutingListenerLock) {
+ sRoutingListener = listener;
+ }
}
/**
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 708d9e10ad73..1d8597497a2c 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -17,17 +17,25 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioFormat;
+import android.media.AudioSystem;
+import android.media.INativeSpatializerCallback;
+import android.media.ISpatializer;
import android.media.ISpatializerCallback;
+import android.media.ISpatializerHeadToSoundStagePoseCallback;
+import android.media.ISpatializerHeadTrackingModeCallback;
import android.media.Spatializer;
+import android.media.SpatializerHeadTrackingMode;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
/**
* A helper class to manage Spatializer related functionality
@@ -35,12 +43,193 @@ import java.util.List;
public class SpatializerHelper {
private static final String TAG = "AS.SpatializerHelper";
+ private static final boolean DEBUG = true;
+
+ private static void logd(String s) {
+ if (DEBUG) {
+ Log.i(TAG, s);
+ }
+ }
+
+ private final @NonNull AudioSystemAdapter mASA;
+ private final @NonNull AudioService mAudioService;
+
+ //------------------------------------------------------------
+ // Spatializer state machine
+ private static final int STATE_UNINITIALIZED = 0;
+ private static final int STATE_NOT_SUPPORTED = 1;
+ private static final int STATE_DISABLED_UNAVAILABLE = 3;
+ private static final int STATE_ENABLED_UNAVAILABLE = 4;
+ private static final int STATE_ENABLED_AVAILABLE = 5;
+ private static final int STATE_DISABLED_AVAILABLE = 6;
+ private int mState = STATE_UNINITIALIZED;
+
+ /** current level as reported by native Spatializer in callback */
+ private int mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ private int mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ private int mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
+ private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
+ private @Nullable ISpatializer mSpat;
+ private @Nullable SpatializerCallback mSpatCallback;
+
+ // default attributes and format that determine basic availability of spatialization
+ private static final AudioAttributes DEFAULT_ATTRIBUTES = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .build();
+ private static final AudioFormat DEFAULT_FORMAT = new AudioFormat.Builder()
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setSampleRate(48000)
+ .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1)
+ .build();
+ // device array to store the routing for the default attributes and format, size 1 because
+ // media is never expected to be duplicated
+ private static final AudioDeviceAttributes[] ROUTING_DEVICES = new AudioDeviceAttributes[1];
//---------------------------------------------------------------
// audio device compatibility / enabled
private final ArrayList<AudioDeviceAttributes> mCompatibleAudioDevices = new ArrayList<>(0);
+ //------------------------------------------------------
+ // initialization
+ SpatializerHelper(@NonNull AudioService mother, @NonNull AudioSystemAdapter asa) {
+ mAudioService = mother;
+ mASA = asa;
+ }
+
+ synchronized void init() {
+ Log.i(TAG, "Initializing");
+ if (mState != STATE_UNINITIALIZED) {
+ throw new IllegalStateException(("init() called in state:" + mState));
+ }
+ // is there a spatializer?
+ mSpatCallback = new SpatializerCallback();
+ final ISpatializer spat = AudioSystem.getSpatializer(mSpatCallback);
+ if (spat == null) {
+ Log.i(TAG, "init(): No Spatializer found");
+ mState = STATE_NOT_SUPPORTED;
+ return;
+ }
+ // capabilities of spatializer?
+ try {
+ byte[] levels = spat.getSupportedLevels();
+ if (levels == null
+ || levels.length == 0
+ || (levels.length == 1
+ && levels[0] == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE)) {
+ Log.e(TAG, "Spatializer is useless");
+ mState = STATE_NOT_SUPPORTED;
+ return;
+ }
+ for (byte level : levels) {
+ logd("found support for level: " + level);
+ if (level == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL) {
+ logd("Setting Spatializer to LEVEL_MULTICHANNEL");
+ mCapableSpatLevel = level;
+ break;
+ }
+ }
+ } catch (RemoteException e) {
+ /* capable level remains at NONE*/
+ } finally {
+ if (spat != null) {
+ try {
+ spat.release();
+ } catch (RemoteException e) { /* capable level remains at NONE*/ }
+ }
+ }
+ if (mCapableSpatLevel == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE) {
+ mState = STATE_NOT_SUPPORTED;
+ return;
+ }
+ mState = STATE_DISABLED_UNAVAILABLE;
+ // note at this point mSpat is still not instantiated
+ }
+
+ /**
+ * Like init() but resets the state and spatializer levels
+ * @param featureEnabled
+ */
+ synchronized void reset(boolean featureEnabled) {
+ Log.i(TAG, "Resetting");
+ mState = STATE_UNINITIALIZED;
+ mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
+ init();
+ setFeatureEnabled(featureEnabled);
+ }
+
+ //------------------------------------------------------
+ // routing monitoring
+ void onRoutingUpdated() {
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ return;
+ case STATE_DISABLED_UNAVAILABLE:
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ case STATE_DISABLED_AVAILABLE:
+ break;
+ }
+ mASA.getDevicesForAttributes(DEFAULT_ATTRIBUTES).toArray(ROUTING_DEVICES);
+ final boolean able =
+ AudioSystem.canBeSpatialized(DEFAULT_ATTRIBUTES, DEFAULT_FORMAT, ROUTING_DEVICES);
+ logd("onRoutingUpdated: can spatialize media 5.1:" + able
+ + " on device:" + ROUTING_DEVICES[0]);
+ setDispatchAvailableState(able);
+ }
+
+ //------------------------------------------------------
+ // spatializer callback from native
+ private final class SpatializerCallback extends INativeSpatializerCallback.Stub {
+
+ public void onLevelChanged(byte level) {
+ logd("SpatializerCallback.onLevelChanged level:" + level);
+ synchronized (SpatializerHelper.this) {
+ mSpatLevel = level;
+ }
+ // TODO use reported spat level to change state
+ }
+
+ public void onHeadTrackingModeChanged(byte mode) {
+ logd("SpatializerCallback.onHeadTrackingModeChanged mode:" + mode);
+ int oldMode, newMode;
+ synchronized (this) {
+ oldMode = mActualHeadTrackingMode;
+ mActualHeadTrackingMode = headTrackingModeTypeToSpatializerInt(mode);
+ newMode = mActualHeadTrackingMode;
+ }
+ if (oldMode != newMode) {
+ dispatchActualHeadTrackingMode(newMode);
+ }
+ }
+
+ public void onHeadToSoundStagePoseUpdated(float[] headToStage) {
+ if (headToStage == null) {
+ Log.e(TAG, "SpatializerCallback.onHeadToStagePoseUpdated null transform");
+ return;
+ }
+ if (headToStage.length != 6) {
+ Log.e(TAG, "SpatializerCallback.onHeadToStagePoseUpdated invalid transform length"
+ + headToStage.length);
+ return;
+ }
+ if (DEBUG) {
+ // 6 values * (4 digits + 1 dot + 2 brackets) = 42 characters
+ StringBuilder t = new StringBuilder(42);
+ for (float val : headToStage) {
+ t.append("[").append(String.format(Locale.ENGLISH, "%.3f", val)).append("]");
+ }
+ logd("SpatializerCallback.onHeadToStagePoseUpdated headToStage:" + t);
+ }
+ dispatchPoseUpdate(headToStage);
+ }
+ };
+
+ //------------------------------------------------------
+ // compatible devices
/**
* @return a shallow copy of the list of compatible audio devices
*/
@@ -59,37 +248,72 @@ public class SpatializerHelper {
}
//------------------------------------------------------
- // enabled state
-
- // global state of feature
- boolean mFeatureEnabled = false;
- // initialized state, checked after each audio_server start
- boolean mInitialized = false;
+ // states
synchronized boolean isEnabled() {
- return mFeatureEnabled;
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ case STATE_DISABLED_UNAVAILABLE:
+ case STATE_DISABLED_AVAILABLE:
+ return false;
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ default:
+ return true;
+ }
}
synchronized boolean isAvailable() {
- if (!mInitialized) {
- return false;
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_DISABLED_UNAVAILABLE:
+ return false;
+ case STATE_DISABLED_AVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ default:
+ return true;
}
- // TODO check device compatibility
- // ...
- return true;
}
- synchronized void setEnabled(boolean enabled) {
- final boolean oldState = mFeatureEnabled;
- mFeatureEnabled = enabled;
- if (oldState != enabled) {
- dispatchEnabledState();
+ synchronized void setFeatureEnabled(boolean enabled) {
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ if (enabled) {
+ throw(new IllegalStateException("Can't enable when uninitialized"));
+ }
+ return;
+ case STATE_NOT_SUPPORTED:
+ if (enabled) {
+ Log.e(TAG, "Can't enable when unsupported");
+ }
+ return;
+ case STATE_DISABLED_UNAVAILABLE:
+ case STATE_DISABLED_AVAILABLE:
+ if (enabled) {
+ createSpat();
+ break;
+ } else {
+ // already in disabled state
+ return;
+ }
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ if (!enabled) {
+ releaseSpat();
+ break;
+ } else {
+ // already in enabled state
+ return;
+ }
}
+ setDispatchFeatureEnabledState(enabled);
}
- public int getImmersiveAudioLevel() {
- // TODO replace placeholder code with actual effect discovery
- return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ synchronized int getCapableImmersiveAudioLevel() {
+ return mCapableSpatLevel;
}
final RemoteCallbackList<ISpatializerCallback> mStateCallbacks =
@@ -105,12 +329,94 @@ public class SpatializerHelper {
mStateCallbacks.unregister(callback);
}
- private synchronized void dispatchEnabledState() {
+ /**
+ * precondition: mState = STATE_*
+ * isFeatureEnabled() != featureEnabled
+ * @param featureEnabled
+ */
+ private synchronized void setDispatchFeatureEnabledState(boolean featureEnabled) {
+ if (featureEnabled) {
+ switch (mState) {
+ case STATE_DISABLED_UNAVAILABLE:
+ mState = STATE_ENABLED_UNAVAILABLE;
+ break;
+ case STATE_DISABLED_AVAILABLE:
+ mState = STATE_ENABLED_AVAILABLE;
+ break;
+ default:
+ throw(new IllegalStateException("Invalid mState:" + mState
+ + " for enabled true"));
+ }
+ } else {
+ switch (mState) {
+ case STATE_ENABLED_UNAVAILABLE:
+ mState = STATE_DISABLED_UNAVAILABLE;
+ break;
+ case STATE_ENABLED_AVAILABLE:
+ mState = STATE_DISABLED_AVAILABLE;
+ break;
+ default:
+ throw (new IllegalStateException("Invalid mState:" + mState
+ + " for enabled false"));
+ }
+ }
+ final int nbCallbacks = mStateCallbacks.beginBroadcast();
+ for (int i = 0; i < nbCallbacks; i++) {
+ try {
+ mStateCallbacks.getBroadcastItem(i)
+ .dispatchSpatializerEnabledChanged(featureEnabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in dispatchSpatializerEnabledChanged", e);
+ }
+ }
+ mStateCallbacks.finishBroadcast();
+ // TODO persist enabled state
+ }
+
+ private synchronized void setDispatchAvailableState(boolean available) {
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ throw(new IllegalStateException(
+ "Should not update available state in state:" + mState));
+ case STATE_DISABLED_UNAVAILABLE:
+ if (available) {
+ mState = STATE_DISABLED_AVAILABLE;
+ break;
+ } else {
+ // already in unavailable state
+ return;
+ }
+ case STATE_ENABLED_UNAVAILABLE:
+ if (available) {
+ mState = STATE_ENABLED_AVAILABLE;
+ break;
+ } else {
+ // already in unavailable state
+ return;
+ }
+ case STATE_DISABLED_AVAILABLE:
+ if (available) {
+ // already in available state
+ return;
+ } else {
+ mState = STATE_DISABLED_UNAVAILABLE;
+ break;
+ }
+ case STATE_ENABLED_AVAILABLE:
+ if (available) {
+ // already in available state
+ return;
+ } else {
+ mState = STATE_ENABLED_UNAVAILABLE;
+ break;
+ }
+ }
final int nbCallbacks = mStateCallbacks.beginBroadcast();
for (int i = 0; i < nbCallbacks; i++) {
try {
mStateCallbacks.getBroadcastItem(i)
- .dispatchSpatializerEnabledChanged(mFeatureEnabled);
+ .dispatchSpatializerAvailableChanged(available);
} catch (RemoteException e) {
Log.e(TAG, "Error in dispatchSpatializerEnabledChanged", e);
}
@@ -119,10 +425,307 @@ public class SpatializerHelper {
}
//------------------------------------------------------
+ // native Spatializer management
+
+ /**
+ * precondition: mState == STATE_DISABLED_*
+ */
+ private void createSpat() {
+ if (mSpat == null) {
+ mSpatCallback = new SpatializerCallback();
+ mSpat = AudioSystem.getSpatializer(mSpatCallback);
+ try {
+ mSpat.setLevel((byte) Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Can't set spatializer level", e);
+ mState = STATE_NOT_SUPPORTED;
+ mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ }
+ }
+ }
+
+ /**
+ * precondition: mState == STATE_ENABLED_*
+ */
+ private void releaseSpat() {
+ if (mSpat != null) {
+ mSpatCallback = null;
+ try {
+ mSpat.release();
+ mSpat = null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Can't set release spatializer cleanly", e);
+ }
+ }
+ }
+
+ //------------------------------------------------------
// virtualization capabilities
synchronized boolean canBeSpatialized(
@NonNull AudioAttributes attributes, @NonNull AudioFormat format) {
- // TODO hook up to spatializer effect for query
- return false;
+ logd("canBeSpatialized usage:" + attributes.getUsage()
+ + " format:" + format.toLogFriendlyString());
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_DISABLED_UNAVAILABLE:
+ logd("canBeSpatialized false due to state:" + mState);
+ return false;
+ case STATE_DISABLED_AVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ break;
+ }
+
+ // filter on AudioAttributes usage
+ switch (attributes.getUsage()) {
+ case AudioAttributes.USAGE_MEDIA:
+ case AudioAttributes.USAGE_GAME:
+ break;
+ default:
+ logd("canBeSpatialized false due to usage:" + attributes.getUsage());
+ return false;
+ }
+ AudioDeviceAttributes[] devices =
+ // going through adapter to take advantage of routing cache
+ (AudioDeviceAttributes[]) mASA.getDevicesForAttributes(attributes).toArray();
+ final boolean able = AudioSystem.canBeSpatialized(attributes, format, devices);
+ logd("canBeSpatialized returning " + able);
+ return able;
+ }
+
+ //------------------------------------------------------
+ // head tracking
+ final RemoteCallbackList<ISpatializerHeadTrackingModeCallback> mHeadTrackingModeCallbacks =
+ new RemoteCallbackList<ISpatializerHeadTrackingModeCallback>();
+
+ synchronized void registerHeadTrackingModeCallback(
+ @NonNull ISpatializerHeadTrackingModeCallback callback) {
+ mHeadTrackingModeCallbacks.register(callback);
+ }
+
+ synchronized void unregisterHeadTrackingModeCallback(
+ @NonNull ISpatializerHeadTrackingModeCallback callback) {
+ mHeadTrackingModeCallbacks.unregister(callback);
+ }
+
+ synchronized int[] getSupportedHeadTrackingModes() {
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ return new int[0];
+ case STATE_NOT_SUPPORTED:
+ // return an empty list when Spatializer functionality is not supported
+ // because the list of head tracking modes you can set is actually empty
+ // as defined in {@link Spatializer#getSupportedHeadTrackingModes()}
+ return new int[0];
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_DISABLED_UNAVAILABLE:
+ case STATE_DISABLED_AVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ if (mSpat == null) {
+ return new int[0];
+ }
+ break;
+ }
+ // mSpat != null
+ try {
+ final byte[] values = mSpat.getSupportedHeadTrackingModes();
+ ArrayList<Integer> list = new ArrayList<>(0);
+ for (byte value : values) {
+ switch (value) {
+ case SpatializerHeadTrackingMode.OTHER:
+ case SpatializerHeadTrackingMode.DISABLED:
+ // not expected here, skip
+ break;
+ case SpatializerHeadTrackingMode.RELATIVE_WORLD:
+ case SpatializerHeadTrackingMode.RELATIVE_SCREEN:
+ list.add(headTrackingModeTypeToSpatializerInt(value));
+ break;
+ default:
+ Log.e(TAG, "Unexpected head tracking mode:" + value,
+ new IllegalArgumentException("invalid mode"));
+ break;
+ }
+ }
+ int[] modes = new int[list.size()];
+ for (int i = 0; i < list.size(); i++) {
+ modes[i] = list.get(i);
+ }
+ return modes;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getSupportedHeadTrackingModes", e);
+ return new int[] { Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED };
+ }
+ }
+
+ synchronized int getActualHeadTrackingMode() {
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ return Spatializer.HEAD_TRACKING_MODE_DISABLED;
+ case STATE_NOT_SUPPORTED:
+ return Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_DISABLED_UNAVAILABLE:
+ case STATE_DISABLED_AVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ if (mSpat == null) {
+ return Spatializer.HEAD_TRACKING_MODE_DISABLED;
+ }
+ break;
+ }
+ // mSpat != null
+ try {
+ return headTrackingModeTypeToSpatializerInt(mSpat.getActualHeadTrackingMode());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling getActualHeadTrackingMode", e);
+ return Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
+ }
+ }
+
+ synchronized int getDesiredHeadTrackingMode() {
+ return mDesiredHeadTrackingMode;
+ }
+
+ synchronized void setGlobalTransform(@NonNull float[] transform) {
+ if (transform.length != 6) {
+ throw new IllegalArgumentException("invalid array size" + transform.length);
+ }
+ if (!checkSpatForHeadTracking("setGlobalTransform")) {
+ return;
+ }
+ try {
+ mSpat.setGlobalTransform(transform);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling setGlobalTransform", e);
+ }
+ }
+
+ synchronized void recenterHeadTracker() {
+ if (!checkSpatForHeadTracking("recenterHeadTracker")) {
+ return;
+ }
+ try {
+ mSpat.recenterHeadTracker();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling recenterHeadTracker", e);
+ }
+ }
+
+ synchronized void setDesiredHeadTrackingMode(@Spatializer.HeadTrackingModeSet int mode) {
+ if (!checkSpatForHeadTracking("setDesiredHeadTrackingMode")) {
+ return;
+ }
+ try {
+ if (mode != mDesiredHeadTrackingMode) {
+ mSpat.setDesiredHeadTrackingMode(spatializerIntToHeadTrackingModeType(mode));
+ mDesiredHeadTrackingMode = mode;
+ dispatchDesiredHeadTrackingMode(mode);
+ }
+
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling setDesiredHeadTrackingMode", e);
+ }
+ }
+
+ private int headTrackingModeTypeToSpatializerInt(byte mode) {
+ switch (mode) {
+ case SpatializerHeadTrackingMode.OTHER:
+ return Spatializer.HEAD_TRACKING_MODE_OTHER;
+ case SpatializerHeadTrackingMode.DISABLED:
+ return Spatializer.HEAD_TRACKING_MODE_DISABLED;
+ case SpatializerHeadTrackingMode.RELATIVE_WORLD:
+ return Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD;
+ case SpatializerHeadTrackingMode.RELATIVE_SCREEN:
+ return Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE;
+ default:
+ throw(new IllegalArgumentException("Unexpected head tracking mode:" + mode));
+ }
+ }
+
+ private byte spatializerIntToHeadTrackingModeType(int sdkMode) {
+ switch (sdkMode) {
+ case Spatializer.HEAD_TRACKING_MODE_OTHER:
+ return SpatializerHeadTrackingMode.OTHER;
+ case Spatializer.HEAD_TRACKING_MODE_DISABLED:
+ return SpatializerHeadTrackingMode.DISABLED;
+ case Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD:
+ return SpatializerHeadTrackingMode.RELATIVE_WORLD;
+ case Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE:
+ return SpatializerHeadTrackingMode.RELATIVE_SCREEN;
+ default:
+ throw(new IllegalArgumentException("Unexpected head tracking mode:" + sdkMode));
+ }
+ }
+
+ private boolean checkSpatForHeadTracking(String funcName) {
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ return false;
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_DISABLED_UNAVAILABLE:
+ case STATE_DISABLED_AVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ if (mSpat == null) {
+ throw (new IllegalStateException(
+ "null Spatializer when calling " + funcName));
+ }
+ break;
+ }
+ return true;
+ }
+
+ private void dispatchActualHeadTrackingMode(int newMode) {
+ final int nbCallbacks = mHeadTrackingModeCallbacks.beginBroadcast();
+ for (int i = 0; i < nbCallbacks; i++) {
+ try {
+ mHeadTrackingModeCallbacks.getBroadcastItem(i)
+ .dispatchSpatializerActualHeadTrackingModeChanged(newMode);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in dispatchSpatializerActualHeadTrackingModeChanged", e);
+ }
+ }
+ mHeadTrackingModeCallbacks.finishBroadcast();
+ }
+
+ private void dispatchDesiredHeadTrackingMode(int newMode) {
+ final int nbCallbacks = mHeadTrackingModeCallbacks.beginBroadcast();
+ for (int i = 0; i < nbCallbacks; i++) {
+ try {
+ mHeadTrackingModeCallbacks.getBroadcastItem(i)
+ .dispatchSpatializerDesiredHeadTrackingModeChanged(newMode);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in dispatchSpatializerDesiredHeadTrackingModeChanged", e);
+ }
+ }
+ mHeadTrackingModeCallbacks.finishBroadcast();
+ }
+
+ //------------------------------------------------------
+ // head pose
+ final RemoteCallbackList<ISpatializerHeadToSoundStagePoseCallback> mHeadPoseCallbacks =
+ new RemoteCallbackList<ISpatializerHeadToSoundStagePoseCallback>();
+
+ synchronized void registerHeadToSoundstagePoseCallback(
+ @NonNull ISpatializerHeadToSoundStagePoseCallback callback) {
+ mHeadPoseCallbacks.register(callback);
+ }
+
+ synchronized void unregisterHeadToSoundstagePoseCallback(
+ @NonNull ISpatializerHeadToSoundStagePoseCallback callback) {
+ mHeadPoseCallbacks.unregister(callback);
+ }
+
+ private void dispatchPoseUpdate(float[] pose) {
+ final int nbCallbacks = mHeadPoseCallbacks.beginBroadcast();
+ for (int i = 0; i < nbCallbacks; i++) {
+ try {
+ mHeadPoseCallbacks.getBroadcastItem(i)
+ .dispatchPoseChanged(pose);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in dispatchPoseChanged", e);
+ }
+ }
+ mHeadPoseCallbacks.finishBroadcast();
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index b42f8980d1c0..9c8ccd946b7f 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -47,6 +47,7 @@ import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -766,8 +767,9 @@ public class AuthService extends SystemService {
if (isUdfps && udfpsProps.length == 3) {
return new FingerprintSensorPropertiesInternal(sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
- componentInfo, sensorType, resetLockoutRequiresHardwareAuthToken, udfpsProps[0],
- udfpsProps[1], udfpsProps[2]);
+ componentInfo, sensorType, resetLockoutRequiresHardwareAuthToken,
+ List.of(new SensorLocationInternal("" /* display */,
+ udfpsProps[0], udfpsProps[1], udfpsProps[2])));
} else {
return new FingerprintSensorPropertiesInternal(sensorId,
Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 996f0fd3a55f..4f7c6b012c23 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -50,7 +50,6 @@ import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorProperties;
import android.hardware.biometrics.SensorPropertiesInternal;
-import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
@@ -62,7 +61,6 @@ import android.util.Slog;
import com.android.internal.R;
import com.android.internal.widget.LockPatternUtils;
-import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import java.util.List;
@@ -541,14 +539,4 @@ public class Utils {
throw new IllegalArgumentException("Unknown strength: " + strength);
}
}
-
- public static int getUdfpsAuthReason(@NonNull AuthenticationClient<?> client) {
- if (client.isKeyguard()) {
- return IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD;
- } else if (client.isBiometricPrompt()) {
- return IUdfpsOverlayController.REASON_AUTH_BP;
- } else {
- return IUdfpsOverlayController.REASON_AUTH_FPM_OTHER;
- }
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 6f38ed04cd96..85d6d7fb2f6a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -28,6 +28,7 @@ import android.content.pm.ApplicationInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.os.IBinder;
import android.os.RemoteException;
@@ -457,4 +458,14 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
public boolean wasAuthAttempted() {
return mAuthAttempted;
}
+
+ protected int getShowOverlayReason() {
+ if (isKeyguard()) {
+ return BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
+ } else if (isBiometricPrompt()) {
+ return BiometricOverlayConstants.REASON_AUTH_BP;
+ } else {
+ return BiometricOverlayConstants.REASON_AUTH_OTHER;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 9191b8b55989..2826e0c97305 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -19,7 +19,9 @@ package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -128,4 +130,15 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
public boolean interruptsPrecedingClients() {
return true;
}
+
+ protected int getOverlayReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) {
+ switch (reason) {
+ case FingerprintManager.ENROLL_FIND_SENSOR:
+ return BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR;
+ case FingerprintManager.ENROLL_ENROLL:
+ return BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
+ default:
+ return BiometricOverlayConstants.REASON_UNKNOWN;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java
new file mode 100644
index 000000000000..008717899aba
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+
+/**
+ * Single entry point & holder for controllers managing UI overlays for biometrics.
+ *
+ * For common operations, like {@link #show(int, int, AcquisitionClient)}, modalities are
+ * skipped if they are not present (provided as null via the constructor).
+ *
+ * Use the getters, such as {@link #ifUdfps(OverlayControllerConsumer)}, to get a controller for
+ * operations that are unique to a single modality.
+ */
+public final class SensorOverlays {
+
+ private static final String TAG = "SensorOverlays";
+
+ @NonNull private final Optional<IUdfpsOverlayController> mUdfpsOverlayController;
+ @NonNull private final Optional<ISidefpsController> mSidefpsController;
+
+ /**
+ * Create an overlay controller for each modality.
+ *
+ * @param udfpsOverlayController under display fps or null if not present on device
+ * @param sidefpsController side fps or null if not present on device
+ */
+ public SensorOverlays(
+ @Nullable IUdfpsOverlayController udfpsOverlayController,
+ @Nullable ISidefpsController sidefpsController) {
+ mUdfpsOverlayController = Optional.ofNullable(udfpsOverlayController);
+ mSidefpsController = Optional.ofNullable(sidefpsController);
+ }
+
+ /**
+ * Show the overlay.
+ *
+ * @param sensorId sensor id
+ * @param reason reason for showing
+ * @param client client performing operation
+ */
+ public void show(int sensorId, @BiometricOverlayConstants.ShowReason int reason,
+ @NonNull AcquisitionClient<?> client) {
+ if (mSidefpsController.isPresent()) {
+ try {
+ mSidefpsController.get().show(sensorId, reason);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when showing the side-fps overlay", e);
+ }
+ }
+
+ if (mUdfpsOverlayController.isPresent()) {
+ final IUdfpsOverlayControllerCallback callback =
+ new IUdfpsOverlayControllerCallback.Stub() {
+ @Override
+ public void onUserCanceled() {
+ client.onUserCanceled();
+ }
+ };
+
+ try {
+ mUdfpsOverlayController.get().showUdfpsOverlay(sensorId, reason, callback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when showing the UDFPS overlay", e);
+ }
+ }
+ }
+
+ /**
+ * Hide the overlay.
+ *
+ * @param sensorId sensor id
+ */
+ public void hide(int sensorId) {
+ if (mSidefpsController.isPresent()) {
+ try {
+ mSidefpsController.get().hide(sensorId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when hiding the side-fps overlay", e);
+ }
+ }
+
+ if (mUdfpsOverlayController.isPresent()) {
+ try {
+ mUdfpsOverlayController.get().hideUdfpsOverlay(sensorId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when hiding the UDFPS overlay", e);
+ }
+ }
+ }
+
+ /**
+ * Use the udfps controller, if present.
+ * @param consumer action
+ */
+ public void ifUdfps(OverlayControllerConsumer<IUdfpsOverlayController> consumer) {
+ if (mUdfpsOverlayController.isPresent()) {
+ try {
+ consumer.accept(mUdfpsOverlayController.get());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception using overlay controller", e);
+ }
+ }
+ }
+
+ /**
+ * Consumer for a biometric overlay controller.
+ *
+ * This behaves like a normal {@link Consumer} except that it will trap and log
+ * any thrown {@link RemoteException}.
+ *
+ * @param <T> the type of the input to the operation
+ **/
+ @FunctionalInterface
+ public interface OverlayControllerConsumer<T> {
+ /** Perform the operation. */
+ void accept(T t) throws RemoteException;
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/SidefpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/SidefpsHelper.java
deleted file mode 100644
index 474066c227d2..000000000000
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/SidefpsHelper.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.biometrics.sensors.fingerprint;
-
-import android.annotation.Nullable;
-import android.hardware.fingerprint.ISidefpsController;
-import android.os.RemoteException;
-import android.util.Slog;
-
-/**
- * Contains helper methods for side-fps fingerprint controller.
- */
-public class SidefpsHelper {
- private static final String TAG = "SidefpsHelper";
-
- /**
- * Shows the side-fps affordance
- * @param sidefpsController controller that shows and hides the side-fps affordance
- */
- public static void showOverlay(@Nullable ISidefpsController sidefpsController) {
- if (sidefpsController == null) {
- return;
- }
-
- try {
- sidefpsController.show();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when showing the side-fps overlay", e);
- }
- }
-
- /**
- * Hides the side-fps affordance
- * @param sidefpsController controller that shows and hides the side-fps affordance
- */
- public static void hideOverlay(@Nullable ISidefpsController sidefpsController) {
- if (sidefpsController == null) {
- return;
- }
- try {
- sidefpsController.hide();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when hiding the side-fps overlay", e);
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
index 879c8a0317d7..29661d46f328 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
@@ -17,17 +17,12 @@
package com.android.server.biometrics.sensors.fingerprint;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.server.biometrics.sensors.AcquisitionClient;
-
/**
* Contains helper methods for under-display fingerprint HIDL.
*/
@@ -68,88 +63,6 @@ public class UdfpsHelper {
}
}
- public static int getReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) {
- switch (reason) {
- case FingerprintManager.ENROLL_FIND_SENSOR:
- return IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR;
- case FingerprintManager.ENROLL_ENROLL:
- return IUdfpsOverlayController.REASON_ENROLL_ENROLLING;
- default:
- return IUdfpsOverlayController.REASON_UNKNOWN;
- }
- }
-
- public static void showUdfpsOverlay(int sensorId, int reason,
- @Nullable IUdfpsOverlayController udfpsOverlayController,
- @NonNull AcquisitionClient<?> client) {
- if (udfpsOverlayController == null) {
- return;
- }
-
- final IUdfpsOverlayControllerCallback callback =
- new IUdfpsOverlayControllerCallback.Stub() {
- @Override
- public void onUserCanceled() {
- client.onUserCanceled();
- }
- };
-
- try {
- udfpsOverlayController.showUdfpsOverlay(sensorId, reason, callback);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when showing the UDFPS overlay", e);
- }
- }
-
- public static void hideUdfpsOverlay(int sensorId,
- @Nullable IUdfpsOverlayController udfpsOverlayController) {
- if (udfpsOverlayController == null) {
- return;
- }
- try {
- udfpsOverlayController.hideUdfpsOverlay(sensorId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when hiding the UDFPS overlay", e);
- }
- }
-
- public static void onAcquiredGood(int sensorId,
- @Nullable IUdfpsOverlayController udfpsOverlayController) {
- if (udfpsOverlayController == null) {
- return;
- }
-
- try {
- udfpsOverlayController.onAcquiredGood(sensorId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when sending onAcquiredGood", e);
- }
- }
-
- public static void onEnrollmentProgress(int sensorId, int remaining,
- @Nullable IUdfpsOverlayController udfpsOverlayController) {
- if (udfpsOverlayController == null) {
- return;
- }
- try {
- udfpsOverlayController.onEnrollmentProgress(sensorId, remaining);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when sending onEnrollmentProgress", e);
- }
- }
-
- public static void onEnrollmentHelp(int sensorId,
- @Nullable IUdfpsOverlayController udfpsOverlayController) {
- if (udfpsOverlayController == null) {
- return;
- }
- try {
- udfpsOverlayController.onEnrollmentHelp(sensorId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when sending onEnrollmentHelp", e);
- }
- }
-
public static boolean isValidAcquisitionMessage(@NonNull Context context,
int acquireInfo, int vendorCode) {
return FingerprintManager.getAcquiredString(context, acquireInfo, vendorCode) != null;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 9d911e0a320b..8a11e8d1cc53 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -27,20 +27,20 @@ import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
-import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
import java.util.ArrayList;
@@ -53,7 +53,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
private static final String TAG = "FingerprintAuthenticationClient";
@NonNull private final LockoutCache mLockoutCache;
- @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+ @NonNull private final SensorOverlays mSensorOverlays;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
@NonNull private final CallbackWithProbe<Probe> mALSProbeCallback;
@@ -68,6 +68,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
int sensorId, boolean isStrongBiometric, int statsClient,
@Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache,
@Nullable IUdfpsOverlayController udfpsOverlayController,
+ @Nullable ISidefpsController sidefpsController,
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner,
@@ -77,7 +78,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
false /* isKeyguardBypassEnabled */);
setRequestId(requestId);
mLockoutCache = lockoutCache;
- mUdfpsOverlayController = udfpsOverlayController;
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mSensorProps = sensorProps;
mALSProbeCallback = createALSCallback(false /* startWithClient */);
}
@@ -120,7 +121,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
if (authenticated) {
mState = STATE_STOPPED;
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
} else {
mState = STATE_STARTED_PAUSED_ATTEMPTED;
}
@@ -131,7 +132,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
// For UDFPS, notify SysUI that the illumination can be turned off.
// See AcquiredInfo#GOOD and AcquiredInfo#RETRYING_CAPTURE
if (acquiredInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD) {
- UdfpsHelper.onAcquiredGood(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.ifUdfps(controller -> controller.onAcquiredGood(getSensorId()));
}
super.onAcquired(acquiredInfo, vendorCode);
@@ -145,27 +146,28 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
BiometricNotificationUtils.showBadCalibrationNotification(getContext());
}
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
}
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(), Utils.getUdfpsAuthReason(this),
- mUdfpsOverlayController, this);
+ mSensorOverlays.show(getSensorId(), getShowOverlayReason(), this);
+
try {
mCancellationSignal = getFreshDaemon().authenticate(mOperationId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
+
try {
mCancellationSignal.cancel();
} catch (RemoteException e) {
@@ -235,7 +237,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
Slog.e(TAG, "Remote exception", e);
}
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
@@ -252,7 +254,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
Slog.e(TAG, "Remote exception", e);
}
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index da91cdd981b9..ac3ce896049b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -19,6 +19,7 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.fingerprint.ISession;
@@ -31,7 +32,7 @@ import com.android.server.biometrics.BiometricsProto;
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.DetectionConsumer;
-import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
+import com.android.server.biometrics.sensors.SensorOverlays;
/**
* Performs fingerprint detection without exposing any matching information (e.g. accept/reject
@@ -42,8 +43,7 @@ class FingerprintDetectClient extends AcquisitionClient<ISession> implements Det
private static final String TAG = "FingerprintDetectClient";
private final boolean mIsStrongBiometric;
- @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
-
+ @NonNull private final SensorOverlays mSensorOverlays;
@Nullable private ICancellationSignal mCancellationSignal;
FingerprintDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
@@ -57,7 +57,7 @@ class FingerprintDetectClient extends AcquisitionClient<ISession> implements Det
BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
setRequestId(requestId);
mIsStrongBiometric = isStrongBiometric;
- mUdfpsOverlayController = udfpsOverlayController;
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController*/);
}
@Override
@@ -68,7 +68,8 @@ class FingerprintDetectClient extends AcquisitionClient<ISession> implements Det
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
+
try {
mCancellationSignal.cancel();
} catch (RemoteException e) {
@@ -79,14 +80,13 @@ class FingerprintDetectClient extends AcquisitionClient<ISession> implements Det
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(),
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD,
- mUdfpsOverlayController, this);
+ mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD, this);
+
try {
mCancellationSignal = getFreshDaemon().detectInteraction();
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting finger detect", e);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index c420c5c57241..ccb34aad3198 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -39,8 +39,8 @@ import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
+import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
-import com.android.server.biometrics.sensors.fingerprint.SidefpsHelper;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
@@ -49,8 +49,7 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
private static final String TAG = "FingerprintEnrollClient";
@NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
- @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
- @Nullable private final ISidefpsController mSidefpsController;
+ @NonNull private final SensorOverlays mSensorOverlays;
private final @FingerprintManager.EnrollReason int mEnrollReason;
@Nullable private ICancellationSignal mCancellationSignal;
@@ -63,7 +62,7 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
- @Nullable IUdfpsOverlayController udfpsOvelayController,
+ @Nullable IUdfpsOverlayController udfpsOverlayController,
@Nullable ISidefpsController sidefpsController,
int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason) {
// UDFPS haptics occur when an image is acquired (instead of when the result is known)
@@ -71,8 +70,7 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
!sensorProps.isAnyUdfpsType() /* shouldVibrate */);
mSensorProps = sensorProps;
- mUdfpsOverlayController = udfpsOvelayController;
- mSidefpsController = sidefpsController;
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mMaxTemplatesPerUser = maxTemplatesPerUser;
mEnrollReason = enrollReason;
@@ -91,11 +89,11 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
super.onEnrollResult(identifier, remaining);
- UdfpsHelper.onEnrollmentProgress(getSensorId(), remaining, mUdfpsOverlayController);
+ mSensorOverlays.ifUdfps(
+ controller -> controller.onEnrollmentProgress(getSensorId(), remaining));
if (remaining == 0) {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
}
}
@@ -106,12 +104,14 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
if (acquiredInfo == BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD
&& mSensorProps.isAnyUdfpsType()) {
vibrateSuccess();
- UdfpsHelper.onAcquiredGood(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.ifUdfps(controller -> controller.onAcquiredGood(getSensorId()));
}
- if (UdfpsHelper.isValidAcquisitionMessage(getContext(), acquiredInfo, vendorCode)) {
- UdfpsHelper.onEnrollmentHelp(getSensorId(), mUdfpsOverlayController);
- }
+ mSensorOverlays.ifUdfps(controller -> {
+ if (UdfpsHelper.isValidAcquisitionMessage(getContext(), acquiredInfo, vendorCode)) {
+ controller.onEnrollmentHelp(getSensorId());
+ }
+ });
super.onAcquired(acquiredInfo, vendorCode);
}
@@ -120,8 +120,7 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
public void onError(int errorCode, int vendorCode) {
super.onError(errorCode, vendorCode);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
}
@Override
@@ -133,8 +132,8 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
+
if (mCancellationSignal != null) {
try {
mCancellationSignal.cancel();
@@ -149,10 +148,8 @@ class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(),
- UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
- mUdfpsOverlayController, this);
- SidefpsHelper.showOverlay(mSidefpsController);
+ mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason), this);
+
BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
try {
mCancellationSignal = getFreshDaemon().enroll(
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index ca83dda3bc4e..0defc3fb6a50 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -25,10 +25,12 @@ import android.app.ActivityTaskManager;
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.content.res.TypedArray;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
+import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.biometrics.common.ComponentInfo;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
@@ -145,6 +147,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mActivityTaskManager = ActivityTaskManager.getInstance();
mTaskStackListener = new BiometricTaskStackListener();
+ final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context);
+
for (SensorProps prop : props) {
final int sensorId = prop.commonProps.sensorId;
@@ -164,9 +168,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
componentInfo,
prop.sensorType,
true /* resetLockoutRequiresHardwareAuthToken */,
- prop.sensorLocations[0].sensorLocationX,
- prop.sensorLocations[0].sensorLocationY,
- prop.sensorLocations[0].sensorRadius);
+ !workaroundLocations.isEmpty() ? workaroundLocations :
+ List.of(new SensorLocationInternal(
+ "" /* displayId */,
+ prop.sensorLocations[0].sensorLocationX,
+ prop.sensorLocations[0].sensorLocationY,
+ prop.sensorLocations[0].sensorRadius)));
final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler,
internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher);
@@ -403,7 +410,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
userId, operationId, restricted, opPackageName, cookie,
false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
- mUdfpsOverlayController, allowBackgroundAuthentication,
+ mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication,
mSensors.get(sensorId).getSensorProperties());
scheduleForSensor(sensorId, client, mFingerprintStateCallback);
});
@@ -647,4 +654,45 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
void setTestHalEnabled(boolean enabled) {
mTestHalEnabled = enabled;
}
+
+ // TODO(b/174868353): workaround for gaps in HAL interface (remove and get directly from HAL)
+ // reads values via an overlay instead of querying the HAL
+ @NonNull
+ private List<SensorLocationInternal> getWorkaroundSensorProps(@NonNull Context context) {
+ final List<SensorLocationInternal> sensorLocations = new ArrayList<>();
+
+ final TypedArray sfpsProps = context.getResources().obtainTypedArray(
+ com.android.internal.R.array.config_sfps_sensor_props);
+ for (int i = 0; i < sfpsProps.length(); i++) {
+ final int id = sfpsProps.getResourceId(i, -1);
+ if (id > 0) {
+ final SensorLocationInternal location = parseSensorLocation(
+ context.getResources().obtainTypedArray(id));
+ if (location != null) {
+ sensorLocations.add(location);
+ }
+ }
+ }
+ sfpsProps.recycle();
+
+ return sensorLocations;
+ }
+
+ @Nullable
+ private SensorLocationInternal parseSensorLocation(@Nullable TypedArray array) {
+ if (array == null) {
+ return null;
+ }
+
+ try {
+ return new SensorLocationInternal(
+ array.getString(0),
+ array.getInt(1, 0),
+ array.getInt(2, 0),
+ array.getInt(3, 0));
+ } catch (Exception e) {
+ Slog.w(getTag(), "malformed sensor location", e);
+ }
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index d2882aa4094c..5f2f4cf6ef3c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -629,7 +629,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mContext, mLazyDaemon, token, requestId, listener, userId, operationId,
restricted, opPackageName, cookie, false /* requireConfirmation */,
mSensorProperties.sensorId, isStrongBiometric, statsClient,
- mTaskStackListener, mLockoutTracker, mUdfpsOverlayController,
+ mTaskStackListener, mLockoutTracker,
+ mUdfpsOverlayController, mSidefpsController,
allowBackgroundAuthentication, mSensorProperties);
mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
});
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 79ad8e1a5c70..dd68b4d37e2a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -426,8 +426,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
mSensorProperties = new FingerprintSensorPropertiesInternal(sensorProps.sensorId,
sensorProps.sensorStrength, maxTemplatesAllowed, sensorProps.componentInfo,
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
- resetLockoutRequiresHardwareAuthToken, sensorProps.sensorLocationX,
- sensorProps.sensorLocationY, sensorProps.sensorRadius);
+ resetLockoutRequiresHardwareAuthToken, sensorProps.getAllLocations());
mMockHalResultController = controller;
mUserHasTrust = new SparseBooleanArray();
mTrustManager = context.getSystemService(TrustManager.class);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 7d95ec098fee..3058e2508f5f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -26,16 +26,17 @@ import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
@@ -52,7 +53,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
private static final String TAG = "Biometrics/FingerprintAuthClient";
private final LockoutFrameworkImpl mLockoutFrameworkImpl;
- @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+ @NonNull private final SensorOverlays mSensorOverlays;
@NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
@NonNull private final CallbackWithProbe<Probe> mALSProbeCallback;
@@ -67,6 +68,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
@NonNull TaskStackListener taskStackListener,
@NonNull LockoutFrameworkImpl lockoutTracker,
@Nullable IUdfpsOverlayController udfpsOverlayController,
+ @Nullable ISidefpsController sidefpsController,
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
@@ -76,7 +78,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
false /* isKeyguardBypassEnabled */);
setRequestId(requestId);
mLockoutFrameworkImpl = lockoutTracker;
- mUdfpsOverlayController = udfpsOverlayController;
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mSensorProps = sensorProps;
mALSProbeCallback = createALSCallback(false /* startWithClient */);
}
@@ -112,7 +114,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
if (authenticated) {
mState = STATE_STOPPED;
resetFailedAttempts(getTargetUserId());
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
} else {
mState = STATE_STARTED_PAUSED_ATTEMPTED;
final @LockoutTracker.LockoutMode int lockoutMode =
@@ -125,7 +127,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
// Send the error, but do not invoke the FinishCallback yet. Since lockout is not
// controlled by the HAL, the framework must stop the sensor before finishing the
// client.
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */);
cancel();
}
@@ -140,7 +142,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
BiometricNotificationUtils.showBadCalibrationNotification(getContext());
}
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
}
private void resetFailedAttempts(int userId) {
@@ -168,8 +170,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(), Utils.getUdfpsAuthReason(this),
- mUdfpsOverlayController, this);
+ mSensorOverlays.show(getSensorId(), getShowOverlayReason(), this);
+
try {
// GroupId was never used. In fact, groupId is always the same as userId.
getFreshDaemon().authenticate(mOperationId, getTargetUserId());
@@ -177,14 +179,15 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
+
try {
getFreshDaemon().cancel();
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 147a20699b54..b854fb300ece 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricOverlayConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -33,6 +34,7 @@ import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.PerformanceTracker;
+import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
@@ -48,7 +50,7 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
private static final String TAG = "FingerprintDetectClient";
private final boolean mIsStrongBiometric;
- @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+ @NonNull private final SensorOverlays mSensorOverlays;
private boolean mIsPointerDown;
public FingerprintDetectClient(@NonNull Context context,
@@ -61,13 +63,14 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT,
BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
setRequestId(requestId);
- mUdfpsOverlayController = udfpsOverlayController;
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */);
mIsStrongBiometric = isStrongBiometric;
}
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
+
try {
getFreshDaemon().cancel();
} catch (RemoteException e) {
@@ -86,16 +89,15 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint>
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(),
- IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD,
- mUdfpsOverlayController, this);
+ mSensorOverlays.show(getSensorId(), BiometricOverlayConstants.REASON_AUTH_KEYGUARD, this);
+
try {
getFreshDaemon().authenticate(0 /* operationId */, getTargetUserId());
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index dc705346f534..1ebf44ca707f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -35,7 +35,7 @@ import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
-import com.android.server.biometrics.sensors.fingerprint.SidefpsHelper;
+import com.android.server.biometrics.sensors.SensorOverlays;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
@@ -49,8 +49,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
private static final String TAG = "FingerprintEnrollClient";
- @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
- @Nullable private final ISidefpsController mSidefpsController;
+ @NonNull private final SensorOverlays mSensorOverlays;
private final @FingerprintManager.EnrollReason int mEnrollReason;
private boolean mIsPointerDown;
@@ -65,8 +64,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
true /* shouldVibrate */);
- mUdfpsOverlayController = udfpsOverlayController;
- mSidefpsController = sidefpsController;
+ mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
mEnrollReason = enrollReason;
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
@@ -95,10 +93,8 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(),
- UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
- mUdfpsOverlayController, this);
- SidefpsHelper.showOverlay(mSidefpsController);
+ mSensorOverlays.show(getSensorId(), getOverlayReasonFromEnrollReason(mEnrollReason), this);
+
BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
try {
// GroupId was never used. In fact, groupId is always the same as userId.
@@ -107,16 +103,15 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
Slog.e(TAG, "Remote exception when requesting enroll", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
+
try {
getFreshDaemon().cancel();
} catch (RemoteException e) {
@@ -131,11 +126,11 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
super.onEnrollResult(identifier, remaining);
- UdfpsHelper.onEnrollmentProgress(getSensorId(), remaining, mUdfpsOverlayController);
+ mSensorOverlays.ifUdfps(
+ controller -> controller.onEnrollmentProgress(getSensorId(), remaining));
if (remaining == 0) {
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
}
}
@@ -143,17 +138,18 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
public void onAcquired(int acquiredInfo, int vendorCode) {
super.onAcquired(acquiredInfo, vendorCode);
- if (UdfpsHelper.isValidAcquisitionMessage(getContext(), acquiredInfo, vendorCode)) {
- UdfpsHelper.onEnrollmentHelp(getSensorId(), mUdfpsOverlayController);
- }
+ mSensorOverlays.ifUdfps(controller -> {
+ if (UdfpsHelper.isValidAcquisitionMessage(getContext(), acquiredInfo, vendorCode)) {
+ controller.onEnrollmentHelp(getSensorId());
+ }
+ });
}
@Override
public void onError(int errorCode, int vendorCode) {
super.onError(errorCode, vendorCode);
- UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
- SidefpsHelper.hideOverlay(mSidefpsController);
+ mSensorOverlays.hide(getSensorId());
}
@Override
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 0f97b9042ebe..f6ba369de769 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -36,9 +36,9 @@ import com.android.server.compat.overrides.ChangeOverrides;
import com.android.server.compat.overrides.OverrideValue;
import com.android.server.compat.overrides.RawOverrideValue;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Represents the state of a single compatibility change.
@@ -82,8 +82,8 @@ public final class CompatChange extends CompatibilityChangeInfo {
ChangeListener mListener = null;
- private Map<String, Boolean> mEvaluatedOverrides;
- private Map<String, PackageOverride> mRawOverrides;
+ private ConcurrentHashMap<String, Boolean> mEvaluatedOverrides;
+ private ConcurrentHashMap<String, PackageOverride> mRawOverrides;
public CompatChange(long changeId) {
this(changeId, null, -1, -1, false, false, null, false);
@@ -114,11 +114,11 @@ public final class CompatChange extends CompatibilityChangeInfo {
description, overridable);
// Initialize override maps.
- mEvaluatedOverrides = new HashMap<>();
- mRawOverrides = new HashMap<>();
+ mEvaluatedOverrides = new ConcurrentHashMap<>();
+ mRawOverrides = new ConcurrentHashMap<>();
}
- void registerListener(ChangeListener listener) {
+ synchronized void registerListener(ChangeListener listener) {
if (mListener != null) {
throw new IllegalStateException(
"Listener for change " + toString() + " already registered.");
@@ -131,8 +131,6 @@ public final class CompatChange extends CompatibilityChangeInfo {
* Force the enabled state of this change for a given package name. The change will only take
* effect after that packages process is killed and restarted.
*
- * <p>Note, this method is not thread safe so callers must ensure thread safety.
- *
* @param pname Package name to enable the change for.
* @param enabled Whether or not to enable the change.
*/
@@ -155,14 +153,12 @@ public final class CompatChange extends CompatibilityChangeInfo {
* Tentatively set the state of this change for a given package name.
* The override will only take effect after that package is installed, if applicable.
*
- * <p>Note, this method is not thread safe so callers must ensure thread safety.
- *
* @param packageName Package name to tentatively enable the change for.
* @param override The package override to be set
* @param allowedState Whether the override is allowed.
* @param versionCode The version code of the package.
*/
- void addPackageOverride(String packageName, PackageOverride override,
+ synchronized void addPackageOverride(String packageName, PackageOverride override,
OverrideAllowedState allowedState, @Nullable Long versionCode) {
if (getLoggingOnly()) {
throw new IllegalArgumentException(
@@ -185,12 +181,12 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @return {@code true} if the recheck yielded a result that requires invalidating caches
* (a deferred override was consolidated or a regular override was removed).
*/
- boolean recheckOverride(String packageName, OverrideAllowedState allowedState,
+ synchronized boolean recheckOverride(String packageName, OverrideAllowedState allowedState,
@Nullable Long versionCode) {
boolean allowed = (allowedState.state == OverrideAllowedState.ALLOWED);
// If the app is not installed or no longer has raw overrides, evaluate to false
- if (versionCode == null || !hasRawOverride(packageName) || !allowed) {
+ if (versionCode == null || !mRawOverrides.containsKey(packageName) || !allowed) {
removePackageOverrideInternal(packageName);
return false;
}
@@ -211,10 +207,6 @@ public final class CompatChange extends CompatibilityChangeInfo {
return true;
}
- boolean hasPackageOverride(String pname) {
- return mRawOverrides.containsKey(pname);
- }
-
/**
* Remove any package override for the given package name, restoring the default behaviour.
*
@@ -224,9 +216,11 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @param allowedState Whether the override is allowed.
* @param versionCode The version code of the package.
*/
- boolean removePackageOverride(String pname, OverrideAllowedState allowedState,
+ synchronized boolean removePackageOverride(String pname, OverrideAllowedState allowedState,
@Nullable Long versionCode) {
- if (mRawOverrides.remove(pname) != null) {
+ if (mRawOverrides.containsKey(pname)) {
+ allowedState.enforce(getId(), pname);
+ mRawOverrides.remove(pname);
recheckOverride(pname, allowedState, versionCode);
return true;
}
@@ -244,8 +238,11 @@ public final class CompatChange extends CompatibilityChangeInfo {
if (app == null) {
return defaultValue();
}
- if (mEvaluatedOverrides.containsKey(app.packageName)) {
- return mEvaluatedOverrides.get(app.packageName);
+ if (app.packageName != null) {
+ final Boolean enabled = mEvaluatedOverrides.get(app.packageName);
+ if (enabled != null) {
+ return enabled;
+ }
}
if (getDisabled()) {
return false;
@@ -269,9 +266,9 @@ public final class CompatChange extends CompatibilityChangeInfo {
* @return {@code true} if the change should be enabled for the package.
*/
boolean willBeEnabled(String packageName) {
- if (hasRawOverride(packageName)) {
- int eval = mRawOverrides.get(packageName).evaluateForAllVersions();
- switch (eval) {
+ final PackageOverride override = mRawOverrides.get(packageName);
+ if (override != null) {
+ switch (override.evaluateForAllVersions()) {
case VALUE_ENABLED:
return true;
case VALUE_DISABLED:
@@ -292,30 +289,12 @@ public final class CompatChange extends CompatibilityChangeInfo {
return !getDisabled();
}
- /**
- * Checks whether a change has an override for a package.
- * @param packageName name of the package
- * @return true if there is such override
- */
- private boolean hasOverride(String packageName) {
- return mEvaluatedOverrides.containsKey(packageName);
- }
-
- /**
- * Checks whether a change has a deferred override for a package.
- * @param packageName name of the package
- * @return true if there is such a deferred override
- */
- private boolean hasRawOverride(String packageName) {
- return mRawOverrides.containsKey(packageName);
- }
-
- void clearOverrides() {
+ synchronized void clearOverrides() {
mRawOverrides.clear();
mEvaluatedOverrides.clear();
}
- void loadOverrides(ChangeOverrides changeOverrides) {
+ synchronized void loadOverrides(ChangeOverrides changeOverrides) {
// Load deferred overrides for backwards compatibility
if (changeOverrides.getDeferred() != null) {
for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
@@ -348,7 +327,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
}
}
- ChangeOverrides saveOverrides() {
+ synchronized ChangeOverrides saveOverrides() {
if (mRawOverrides.isEmpty()) {
return null;
}
@@ -406,7 +385,7 @@ public final class CompatChange extends CompatibilityChangeInfo {
return sb.append(")").toString();
}
- private void notifyListener(String packageName) {
+ private synchronized void notifyListener(String packageName) {
if (mListener != null) {
mListener.onCompatChange(packageName);
}
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 3faffe198ac9..2bf1ccd51939 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -28,7 +28,6 @@ import android.content.pm.PackageManager;
import android.os.Environment;
import android.text.TextUtils;
import android.util.LongArray;
-import android.util.LongSparseArray;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -55,11 +54,12 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.xml.datatype.DatatypeConfigurationException;
@@ -76,9 +76,7 @@ final class CompatConfig {
private static final String STATIC_OVERRIDES_PRODUCT_DIR = "/product/etc/appcompat";
private static final String OVERRIDES_FILE = "compat_framework_overrides.xml";
- private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
- @GuardedBy("mReadWriteLock")
- private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
+ private final ConcurrentHashMap<Long, CompatChange> mChanges = new ConcurrentHashMap<>();
private final OverrideValidatorImpl mOverrideValidator;
private final AndroidBuildClassifier mAndroidBuildClassifier;
@@ -113,21 +111,13 @@ final class CompatConfig {
/**
* Adds a change.
*
- * <p>This is intended to be used by code that reads change config from the filesystem. This
- * should be done at system startup time.
- *
- * <p>Any change with the same ID will be overwritten.
+ * <p>This is intended to be used by unit tests only.
*
* @param change the change to add
*/
+ @VisibleForTesting
void addChange(CompatChange change) {
- mReadWriteLock.writeLock().lock();
- try {
- mChanges.put(change.getId(), change);
- invalidateCache();
- } finally {
- mReadWriteLock.writeLock().unlock();
- }
+ mChanges.put(change.getId(), change);
}
/**
@@ -143,20 +133,14 @@ final class CompatConfig {
*/
long[] getDisabledChanges(ApplicationInfo app) {
LongArray disabled = new LongArray();
- mReadWriteLock.readLock().lock();
- try {
- for (int i = 0; i < mChanges.size(); ++i) {
- CompatChange c = mChanges.valueAt(i);
- if (!c.isEnabled(app, mAndroidBuildClassifier)) {
- disabled.add(c.getId());
- }
+ for (CompatChange c : mChanges.values()) {
+ if (!c.isEnabled(app, mAndroidBuildClassifier)) {
+ disabled.add(c.getId());
}
- } finally {
- mReadWriteLock.readLock().unlock();
}
- // Note: we don't need to explicitly sort the array, as the behaviour of LongSparseArray
- // (mChanges) ensures it's already sorted.
- return disabled.toArray();
+ final long[] sortedChanges = disabled.toArray();
+ Arrays.sort(sortedChanges);
+ return sortedChanges;
}
/**
@@ -166,15 +150,10 @@ final class CompatConfig {
* @return the change ID, or {@code -1} if no change with that name exists
*/
long lookupChangeId(String name) {
- mReadWriteLock.readLock().lock();
- try {
- for (int i = 0; i < mChanges.size(); ++i) {
- if (TextUtils.equals(mChanges.valueAt(i).getName(), name)) {
- return mChanges.keyAt(i);
- }
+ for (CompatChange c : mChanges.values()) {
+ if (TextUtils.equals(c.getName(), name)) {
+ return c.getId();
}
- } finally {
- mReadWriteLock.readLock().unlock();
}
return -1;
}
@@ -188,17 +167,12 @@ final class CompatConfig {
* change ID is not known, as unknown changes are enabled by default.
*/
boolean isChangeEnabled(long changeId, ApplicationInfo app) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- if (c == null) {
- // we know nothing about this change: default behaviour is enabled.
- return true;
- }
- return c.isEnabled(app, mAndroidBuildClassifier);
- } finally {
- mReadWriteLock.readLock().unlock();
+ CompatChange c = mChanges.get(changeId);
+ if (c == null) {
+ // we know nothing about this change: default behaviour is enabled.
+ return true;
}
+ return c.isEnabled(app, mAndroidBuildClassifier);
}
/**
@@ -210,17 +184,12 @@ final class CompatConfig {
* {@code true} if the change ID is not known, as unknown changes are enabled by default.
*/
boolean willChangeBeEnabled(long changeId, String packageName) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- if (c == null) {
- // we know nothing about this change: default behaviour is enabled.
- return true;
- }
- return c.willBeEnabled(packageName);
- } finally {
- mReadWriteLock.readLock().unlock();
+ CompatChange c = mChanges.get(changeId);
+ if (c == null) {
+ // we know nothing about this change: default behaviour is enabled.
+ return true;
}
+ return c.willBeEnabled(packageName);
}
/**
@@ -239,7 +208,7 @@ final class CompatConfig {
* @return {@code true} if the change existed before adding the override
* @throws IllegalStateException if overriding is not allowed
*/
- boolean addOverride(long changeId, String packageName, boolean enabled) {
+ synchronized boolean addOverride(long changeId, String packageName, boolean enabled) {
boolean alreadyKnown = addOverrideUnsafe(changeId, packageName,
new PackageOverride.Builder().setEnabled(enabled).build());
saveOverrides();
@@ -250,12 +219,11 @@ final class CompatConfig {
/**
* Overrides the enabled state for a given change and app.
*
- * <p>Note, package overrides are not persistent and will be lost on system or runtime restart.
*
* @param overrides list of overrides to default changes config.
* @param packageName app for which the overrides will be applied.
*/
- void addOverrides(CompatibilityOverrideConfig overrides, String packageName) {
+ synchronized void addOverrides(CompatibilityOverrideConfig overrides, String packageName) {
for (Long changeId : overrides.overrides.keySet()) {
addOverrideUnsafe(changeId, packageName, overrides.overrides.get(changeId));
}
@@ -265,36 +233,24 @@ final class CompatConfig {
private boolean addOverrideUnsafe(long changeId, String packageName,
PackageOverride overrides) {
- boolean alreadyKnown = true;
+ final AtomicBoolean alreadyKnown = new AtomicBoolean(true);
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
allowedState.enforce(changeId, packageName);
Long versionCode = getVersionCodeOrNull(packageName);
- mReadWriteLock.writeLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- if (c == null) {
- alreadyKnown = false;
- c = new CompatChange(changeId);
- addChange(c);
- }
- c.addPackageOverride(packageName, overrides, allowedState, versionCode);
- invalidateCache();
- } finally {
- mReadWriteLock.writeLock().unlock();
- }
- return alreadyKnown;
+
+ final CompatChange c = mChanges.computeIfAbsent(changeId, (key) -> {
+ alreadyKnown.set(false);
+ return new CompatChange(changeId);
+ });
+ c.addPackageOverride(packageName, overrides, allowedState, versionCode);
+ invalidateCache();
+ return alreadyKnown.get();
}
/** Checks whether the change is known to the compat config. */
boolean isKnownChangeId(long changeId) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- return c != null;
- } finally {
- mReadWriteLock.readLock().unlock();
- }
+ return mChanges.containsKey(changeId);
}
/**
@@ -302,55 +258,35 @@ final class CompatConfig {
* target SDK gated).
*/
int maxTargetSdkForChangeIdOptIn(long changeId) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- if (c != null && c.getEnableSinceTargetSdk() != -1) {
- return c.getEnableSinceTargetSdk() - 1;
- }
- return -1;
- } finally {
- mReadWriteLock.readLock().unlock();
+ CompatChange c = mChanges.get(changeId);
+ if (c != null && c.getEnableSinceTargetSdk() != -1) {
+ return c.getEnableSinceTargetSdk() - 1;
}
+ return -1;
}
/**
* Returns whether the change is marked as logging only.
*/
boolean isLoggingOnly(long changeId) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- return c != null && c.getLoggingOnly();
- } finally {
- mReadWriteLock.readLock().unlock();
- }
+ CompatChange c = mChanges.get(changeId);
+ return c != null && c.getLoggingOnly();
}
/**
* Returns whether the change is marked as disabled.
*/
boolean isDisabled(long changeId) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- return c != null && c.getDisabled();
- } finally {
- mReadWriteLock.readLock().unlock();
- }
+ CompatChange c = mChanges.get(changeId);
+ return c != null && c.getDisabled();
}
/**
* Returns whether the change is overridable.
*/
boolean isOverridable(long changeId) {
- mReadWriteLock.readLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- return c != null && c.getOverridable();
- } finally {
- mReadWriteLock.readLock().unlock();
- }
+ CompatChange c = mChanges.get(changeId);
+ return c != null && c.getOverridable();
}
/**
@@ -363,10 +299,12 @@ final class CompatConfig {
* @param packageName the app package name that was overridden
* @return {@code true} if an override existed;
*/
- boolean removeOverride(long changeId, String packageName) {
+ synchronized boolean removeOverride(long changeId, String packageName) {
boolean overrideExists = removeOverrideUnsafe(changeId, packageName);
- saveOverrides();
- invalidateCache();
+ if (overrideExists) {
+ saveOverrides();
+ invalidateCache();
+ }
return overrideExists;
}
@@ -376,14 +314,9 @@ final class CompatConfig {
*/
private boolean removeOverrideUnsafe(long changeId, String packageName) {
Long versionCode = getVersionCodeOrNull(packageName);
- mReadWriteLock.writeLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- if (c != null) {
- return removeOverrideUnsafe(c, packageName, versionCode);
- }
- } finally {
- mReadWriteLock.writeLock().unlock();
+ CompatChange c = mChanges.get(changeId);
+ if (c != null) {
+ return removeOverrideUnsafe(c, packageName, versionCode);
}
return false;
}
@@ -397,13 +330,7 @@ final class CompatConfig {
long changeId = change.getId();
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
- if (change.hasPackageOverride(packageName)) {
- allowedState.enforce(changeId, packageName);
- change.removePackageOverride(packageName, allowedState, versionCode);
- invalidateCache();
- return true;
- }
- return false;
+ return change.removePackageOverride(packageName, allowedState, versionCode);
}
/**
@@ -414,19 +341,16 @@ final class CompatConfig {
*
* @param packageName the package for which the overrides should be purged
*/
- void removePackageOverrides(String packageName) {
+ synchronized void removePackageOverrides(String packageName) {
Long versionCode = getVersionCodeOrNull(packageName);
- mReadWriteLock.writeLock().lock();
- try {
- for (int i = 0; i < mChanges.size(); ++i) {
- CompatChange change = mChanges.valueAt(i);
- removeOverrideUnsafe(change, packageName, versionCode);
- }
- } finally {
- mReadWriteLock.writeLock().unlock();
+ boolean shouldInvalidateCache = false;
+ for (CompatChange change : mChanges.values()) {
+ shouldInvalidateCache |= removeOverrideUnsafe(change, packageName, versionCode);
+ }
+ if (shouldInvalidateCache) {
+ saveOverrides();
+ invalidateCache();
}
- saveOverrides();
- invalidateCache();
}
/**
@@ -439,34 +363,31 @@ final class CompatConfig {
* @param overridesToRemove list of change IDs for which to restore the default behaviour.
* @param packageName the package for which the overrides should be purged
*/
- void removePackageOverrides(CompatibilityOverridesToRemoveConfig overridesToRemove,
+ synchronized void removePackageOverrides(CompatibilityOverridesToRemoveConfig overridesToRemove,
String packageName) {
+ boolean shouldInvalidateCache = false;
for (Long changeId : overridesToRemove.changeIds) {
- removeOverrideUnsafe(changeId, packageName);
+ shouldInvalidateCache |= removeOverrideUnsafe(changeId, packageName);
+ }
+ if (shouldInvalidateCache) {
+ saveOverrides();
+ invalidateCache();
}
- saveOverrides();
- invalidateCache();
}
private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName,
int targetSdkVersion) {
LongArray allowed = new LongArray();
- mReadWriteLock.readLock().lock();
- try {
- for (int i = 0; i < mChanges.size(); ++i) {
- CompatChange change = mChanges.valueAt(i);
- if (change.getEnableSinceTargetSdk() != targetSdkVersion) {
- continue;
- }
- OverrideAllowedState allowedState =
- mOverrideValidator.getOverrideAllowedState(change.getId(),
- packageName);
- if (allowedState.state == OverrideAllowedState.ALLOWED) {
- allowed.add(change.getId());
- }
+ for (CompatChange change : mChanges.values()) {
+ if (change.getEnableSinceTargetSdk() != targetSdkVersion) {
+ continue;
+ }
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedState(change.getId(),
+ packageName);
+ if (allowedState.state == OverrideAllowedState.ALLOWED) {
+ allowed.add(change.getId());
}
- } finally {
- mReadWriteLock.readLock().unlock();
}
return allowed.toArray();
}
@@ -479,12 +400,15 @@ final class CompatConfig {
*/
int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
+ boolean shouldInvalidateCache = false;
for (long changeId : changes) {
- addOverrideUnsafe(changeId, packageName,
+ shouldInvalidateCache |= addOverrideUnsafe(changeId, packageName,
new PackageOverride.Builder().setEnabled(true).build());
}
- saveOverrides();
- invalidateCache();
+ if (shouldInvalidateCache) {
+ saveOverrides();
+ invalidateCache();
+ }
return changes.length;
}
@@ -496,30 +420,27 @@ final class CompatConfig {
*/
int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
+ boolean shouldInvalidateCache = false;
for (long changeId : changes) {
- addOverrideUnsafe(changeId, packageName,
+ shouldInvalidateCache |= addOverrideUnsafe(changeId, packageName,
new PackageOverride.Builder().setEnabled(false).build());
}
- saveOverrides();
- invalidateCache();
+ if (shouldInvalidateCache) {
+ saveOverrides();
+ invalidateCache();
+ }
return changes.length;
}
boolean registerListener(long changeId, CompatChange.ChangeListener listener) {
- boolean alreadyKnown = true;
- mReadWriteLock.writeLock().lock();
- try {
- CompatChange c = mChanges.get(changeId);
- if (c == null) {
- alreadyKnown = false;
- c = new CompatChange(changeId);
- addChange(c);
- }
- c.registerListener(listener);
- } finally {
- mReadWriteLock.writeLock().unlock();
- }
- return alreadyKnown;
+ final AtomicBoolean alreadyKnown = new AtomicBoolean(true);
+ final CompatChange c = mChanges.computeIfAbsent(changeId, (key) -> {
+ alreadyKnown.set(false);
+ invalidateCache();
+ return new CompatChange(changeId);
+ });
+ c.registerListener(listener);
+ return alreadyKnown.get();
}
boolean defaultChangeIdValue(long changeId) {
@@ -537,12 +458,7 @@ final class CompatConfig {
@VisibleForTesting
void clearChanges() {
- mReadWriteLock.writeLock().lock();
- try {
- mChanges.clear();
- } finally {
- mReadWriteLock.writeLock().unlock();
- }
+ mChanges.clear();
}
/**
@@ -551,18 +467,12 @@ final class CompatConfig {
* @param pw {@link PrintWriter} instance to which the information will be dumped
*/
void dumpConfig(PrintWriter pw) {
- mReadWriteLock.readLock().lock();
- try {
- if (mChanges.size() == 0) {
- pw.println("No compat overrides.");
- return;
- }
- for (int i = 0; i < mChanges.size(); ++i) {
- CompatChange c = mChanges.valueAt(i);
- pw.println(c.toString());
- }
- } finally {
- mReadWriteLock.readLock().unlock();
+ if (mChanges.size() == 0) {
+ pw.println("No compat overrides.");
+ return;
+ }
+ for (CompatChange c : mChanges.values()) {
+ pw.println(c.toString());
}
}
@@ -574,18 +484,12 @@ final class CompatConfig {
CompatibilityChangeConfig getAppConfig(ApplicationInfo applicationInfo) {
Set<Long> enabled = new HashSet<>();
Set<Long> disabled = new HashSet<>();
- mReadWriteLock.readLock().lock();
- try {
- for (int i = 0; i < mChanges.size(); ++i) {
- CompatChange c = mChanges.valueAt(i);
- if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) {
- enabled.add(c.getId());
- } else {
- disabled.add(c.getId());
- }
+ for (CompatChange c : mChanges.values()) {
+ if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) {
+ enabled.add(c.getId());
+ } else {
+ disabled.add(c.getId());
}
- } finally {
- mReadWriteLock.readLock().unlock();
}
return new CompatibilityChangeConfig(new ChangeConfig(enabled, disabled));
}
@@ -596,17 +500,12 @@ final class CompatConfig {
* @return an array of {@link CompatibilityChangeInfo} with the current changes
*/
CompatibilityChangeInfo[] dumpChanges() {
- mReadWriteLock.readLock().lock();
- try {
- CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()];
- for (int i = 0; i < mChanges.size(); ++i) {
- CompatChange change = mChanges.valueAt(i);
- changeInfos[i] = new CompatibilityChangeInfo(change);
- }
- return changeInfos;
- } finally {
- mReadWriteLock.readLock().unlock();
+ CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()];
+ int i = 0;
+ for (CompatChange change : mChanges.values()) {
+ changeInfos[i++] = new CompatibilityChangeInfo(change);
}
+ return changeInfos;
}
void initConfigFromLib(File libraryDir) {
@@ -626,10 +525,12 @@ final class CompatConfig {
Config config = com.android.server.compat.config.XmlParser.read(in);
for (Change change : config.getCompatChange()) {
Slog.d(TAG, "Adding: " + change.toString());
- addChange(new CompatChange(change));
+ mChanges.put(change.getId(), new CompatChange(change));
}
} catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
Slog.e(TAG, "Encountered an error while reading/parsing compat config file", e);
+ } finally {
+ invalidateCache();
}
}
@@ -641,15 +542,12 @@ final class CompatConfig {
@VisibleForTesting
void initOverrides(File dynamicOverridesFile, File staticOverridesFile) {
// Clear overrides from all changes before loading.
- mReadWriteLock.writeLock().lock();
- try {
- for (int i = 0; i < mChanges.size(); ++i) {
- mChanges.valueAt(i).clearOverrides();
- }
- } finally {
- mReadWriteLock.writeLock().unlock();
+
+ for (CompatChange c : mChanges.values()) {
+ c.clearOverrides();
}
+
loadOverrides(staticOverridesFile);
mOverridesFile = dynamicOverridesFile;
@@ -698,18 +596,12 @@ final class CompatConfig {
}
synchronized (mOverridesFile) {
Overrides overrides = new Overrides();
- mReadWriteLock.readLock().lock();
- try {
- List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides();
- for (int idx = 0; idx < mChanges.size(); ++idx) {
- CompatChange c = mChanges.valueAt(idx);
- ChangeOverrides changeOverrides = c.saveOverrides();
- if (changeOverrides != null) {
- changeOverridesList.add(changeOverrides);
- }
+ List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides();
+ for (CompatChange c : mChanges.values()) {
+ ChangeOverrides changeOverrides = c.saveOverrides();
+ if (changeOverrides != null) {
+ changeOverridesList.add(changeOverrides);
}
- } finally {
- mReadWriteLock.readLock().unlock();
}
// Create the file if it doesn't already exist
try {
@@ -741,20 +633,11 @@ final class CompatConfig {
void recheckOverrides(String packageName) {
Long versionCode = getVersionCodeOrNull(packageName);
boolean shouldInvalidateCache = false;
- mReadWriteLock.readLock().lock();
- try {
- for (int idx = 0; idx < mChanges.size(); ++idx) {
- CompatChange c = mChanges.valueAt(idx);
- if (!c.hasPackageOverride(packageName)) {
- continue;
- }
- OverrideAllowedState allowedState =
- mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(),
- packageName);
- shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, versionCode);
- }
- } finally {
- mReadWriteLock.readLock().unlock();
+ for (CompatChange c : mChanges.values()) {
+ OverrideAllowedState allowedState =
+ mOverrideValidator.getOverrideAllowedStateForRecheck(c.getId(),
+ packageName);
+ shouldInvalidateCache |= c.recheckOverride(packageName, allowedState, versionCode);
}
if (shouldInvalidateCache) {
invalidateCache();
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 806a5dd65a13..792feea01e27 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -31,6 +31,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManagerInternal;
import android.hardware.devicestate.IDeviceStateManager;
import android.hardware.devicestate.IDeviceStateManagerCallback;
import android.os.Binder;
@@ -161,6 +162,7 @@ public final class DeviceStateManagerService extends SystemService {
@Override
public void onStart() {
publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
+ publishLocalService(DeviceStateManagerInternal.class, new LocalService());
}
@VisibleForTesting
@@ -240,13 +242,6 @@ public final class DeviceStateManagerService extends SystemService {
}
/** Returns the list of currently supported device state identifiers. */
- private int[] getSupportedStateIdentifiers() {
- synchronized (mLock) {
- return getSupportedStateIdentifiersLocked();
- }
- }
-
- /** Returns the list of currently supported device state identifiers. */
private int[] getSupportedStateIdentifiersLocked() {
int[] supportedStates = new int[mDeviceStates.size()];
for (int i = 0; i < supportedStates.length; i++) {
@@ -848,4 +843,14 @@ public final class DeviceStateManagerService extends SystemService {
}
}
}
+
+ /** Implementation of {@link DeviceStateManagerInternal} published as a local service. */
+ private final class LocalService extends DeviceStateManagerInternal {
+ @Override
+ public int[] getSupportedStateIdentifiers() {
+ synchronized (mLock) {
+ return getSupportedStateIdentifiersLocked();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 73bcea6de115..f16ed41af5ca 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -53,6 +53,7 @@ import android.graphics.Point;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManagerInternal;
import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
import android.hardware.display.BrightnessConfiguration;
@@ -131,6 +132,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
@@ -210,6 +212,7 @@ public final class DisplayManagerService extends SystemService {
private WindowManagerInternal mWindowManagerInternal;
private InputManagerInternal mInputManagerInternal;
private IMediaProjectionManager mProjectionService;
+ private DeviceStateManagerInternal mDeviceStateManager;
private int[] mUserDisabledHdrTypes = {};
private boolean mAreUserDisabledHdrTypesAllowed = true;
@@ -557,10 +560,9 @@ public final class DisplayManagerService extends SystemService {
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
- DeviceStateManager deviceStateManager =
- mContext.getSystemService(DeviceStateManager.class);
- deviceStateManager.registerCallback(new HandlerExecutor(mHandler),
- new DeviceStateListener());
+ mDeviceStateManager = LocalServices.getService(DeviceStateManagerInternal.class);
+ mContext.getSystemService(DeviceStateManager.class).registerCallback(
+ new HandlerExecutor(mHandler), new DeviceStateListener());
scheduleTraversalLocked(false);
}
@@ -2252,6 +2254,9 @@ public final class DisplayManagerService extends SystemService {
int displayId = msg.arg1;
final LogicalDisplay display =
mLogicalDisplayMapper.getDisplayLocked(displayId);
+ if (display == null) {
+ break;
+ }
uids = display.getPendingFrameRateOverrideUids();
display.clearPendingFrameRateOverrideUids();
}
@@ -3271,6 +3276,53 @@ public final class DisplayManagerService extends SystemService {
}
@Override
+ public Set<DisplayInfo> getPossibleDisplayInfo(int displayId) {
+ synchronized (mSyncRoot) {
+ // Retrieve the group associated with this display id.
+ final int displayGroupId =
+ mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(displayId);
+ if (displayGroupId == Display.INVALID_DISPLAY_GROUP) {
+ Slog.w(TAG,
+ "Can't get possible display info since display group for " + displayId
+ + " does not exist");
+ return new ArraySet<>();
+ }
+
+ // Assume any display in this group can be swapped out for the given display id.
+ Set<DisplayInfo> possibleInfo = new ArraySet<>();
+ final DisplayGroup group = mLogicalDisplayMapper.getDisplayGroupLocked(
+ displayGroupId);
+ for (int i = 0; i < group.getSizeLocked(); i++) {
+ final int id = group.getIdLocked(i);
+ final LogicalDisplay logical = mLogicalDisplayMapper.getDisplayLocked(id);
+ if (logical == null) {
+ Slog.w(TAG,
+ "Can't get possible display info since logical display for "
+ + "display id " + id + " does not exist, as part of group "
+ + displayGroupId);
+ } else {
+ possibleInfo.add(logical.getDisplayInfoLocked());
+ }
+ }
+
+ // For the supported device states, retrieve the DisplayInfos for the logical
+ // display layout.
+ if (mDeviceStateManager == null) {
+ Slog.w(TAG, "Can't get supported states since DeviceStateManager not ready");
+ } else {
+ final int[] supportedStates =
+ mDeviceStateManager.getSupportedStateIdentifiers();
+ for (int state : supportedStates) {
+ possibleInfo.addAll(
+ mLogicalDisplayMapper.getDisplayInfoForStateLocked(state, displayId,
+ displayGroupId));
+ }
+ }
+ return possibleInfo;
+ }
+ }
+
+ @Override
public Point getDisplayPosition(int displayId) {
synchronized (mSyncRoot) {
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index a93171835365..973dcc4c79e5 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -24,6 +24,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.SystemProperties;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
@@ -38,6 +39,7 @@ import com.android.server.display.layout.Layout;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.Set;
import java.util.function.Consumer;
/**
@@ -254,6 +256,61 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
return mDisplayGroups.get(groupId);
}
+ /**
+ * Returns the set of {@link DisplayInfo} for this device state, only fetching the info that is
+ * part of the same display group as the provided display id. The DisplayInfo represent the
+ * logical display layouts possible for the given device state.
+ *
+ * @param deviceState the state to query possible layouts for
+ * @param displayId the display id to apply to all displays within the group
+ * @param groupId the display group to filter display info for. Must be the same group as
+ * the display with the provided display id.
+ */
+ public Set<DisplayInfo> getDisplayInfoForStateLocked(int deviceState, int displayId,
+ int groupId) {
+ Set<DisplayInfo> displayInfos = new ArraySet<>();
+ final Layout layout = mDeviceStateToLayoutMap.get(deviceState);
+ final int layoutSize = layout.size();
+ for (int i = 0; i < layoutSize; i++) {
+ Layout.Display displayLayout = layout.getAt(i);
+ if (displayLayout == null) {
+ continue;
+ }
+
+ // If the underlying display-device we want to use for this display
+ // doesn't exist, then skip it. This can happen at startup as display-devices
+ // trickle in one at a time. When the new display finally shows up, the layout is
+ // recalculated so that the display is properly added to the current layout.
+ final DisplayAddress address = displayLayout.getAddress();
+ final DisplayDevice device = mDisplayDeviceRepo.getByAddressLocked(address);
+ if (device == null) {
+ Slog.w(TAG, "The display device (" + address + "), is not available"
+ + " for the display state " + deviceState);
+ continue;
+ }
+
+ // Find or create the LogicalDisplay to map the DisplayDevice to.
+ final int logicalDisplayId = displayLayout.getLogicalDisplayId();
+ final LogicalDisplay logicalDisplay = getDisplayLocked(logicalDisplayId);
+ if (logicalDisplay == null) {
+ Slog.w(TAG, "The logical display (" + address + "), is not available"
+ + " for the display state " + deviceState);
+ continue;
+ }
+ final DisplayInfo temp = logicalDisplay.getDisplayInfoLocked();
+ DisplayInfo displayInfo = new DisplayInfo(temp);
+ if (displayInfo.displayGroupId != groupId) {
+ // Ignore any displays not in the provided group.
+ continue;
+ }
+ // A display in the same group can be swapped out at any point, so set the display id
+ // for all results to the provided display id.
+ displayInfo.displayId = displayId;
+ displayInfos.add(displayInfo);
+ }
+ return displayInfos;
+ }
+
public void dumpLocked(PrintWriter pw) {
pw.println("LogicalDisplayMapper:");
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 688a3b2f1d59..8c7d257d271b 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -31,6 +31,8 @@ import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING;
import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING;
import static android.os.PowerManagerInternal.wakefulnessToString;
+import static com.android.internal.util.LatencyTracker.ACTION_TURN_ON_SCREEN;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -104,6 +106,7 @@ import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.LatencyTracker;
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.LockGuard;
@@ -1842,6 +1845,9 @@ public final class PowerManagerService extends SystemService
+ ", details=" + details
+ ")...");
Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId);
+ // The instrument will be timed out automatically after 2 seconds.
+ LatencyTracker.getInstance(mContext)
+ .onActionStart(ACTION_TURN_ON_SCREEN, String.valueOf(groupId));
setWakefulnessLocked(groupId, WAKEFULNESS_AWAKE, eventTime, uid, reason, opUid,
opPackageName, details);
@@ -3225,6 +3231,7 @@ public final class PowerManagerService extends SystemService
&& mDisplayGroupPowerStateMapper.getWakefulnessLocked(
groupId) == WAKEFULNESS_AWAKE) {
mDisplayGroupPowerStateMapper.setPoweringOnLocked(groupId, false);
+ LatencyTracker.getInstance(mContext).onActionEnd(ACTION_TURN_ON_SCREEN);
Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, groupId);
final int latencyMs = (int) (mClock.uptimeMillis()
- mDisplayGroupPowerStateMapper.getLastPowerOnTimeLocked(groupId));
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 382398a210bb..e0cc8e182079 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -352,7 +352,7 @@ public class Vcn extends Handler {
}
private void handleSafeModeStatusChanged() {
- logDbg("VcnGatewayConnection safe mode status changed");
+ logVdbg("VcnGatewayConnection safe mode status changed");
boolean hasSafeModeGatewayConnection = false;
// If any VcnGatewayConnection is in safe mode, mark the entire VCN as being in safe mode
@@ -368,7 +368,7 @@ public class Vcn extends Handler {
hasSafeModeGatewayConnection ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE;
if (oldStatus != mCurrentStatus) {
mVcnCallback.onSafeModeStatusChanged(hasSafeModeGatewayConnection);
- logDbg(
+ logInfo(
"Safe mode "
+ (mCurrentStatus == VCN_STATUS_CODE_SAFE_MODE ? "entered" : "exited"));
}
@@ -539,6 +539,16 @@ public class Vcn extends Handler {
Slog.d(TAG, getLogPrefix() + msg, tr);
}
+ private void logInfo(String msg) {
+ Slog.i(TAG, getLogPrefix() + msg);
+ LOCAL_LOG.log(getLogPrefix() + "INFO: " + msg);
+ }
+
+ private void logInfo(String msg, Throwable tr) {
+ Slog.i(TAG, getLogPrefix() + msg, tr);
+ LOCAL_LOG.log(getLogPrefix() + "INFO: " + msg + tr);
+ }
+
private void logErr(String msg) {
Slog.e(TAG, getLogPrefix() + msg);
LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg);
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 450257fcdecb..7dec4e785f5c 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1677,10 +1677,8 @@ public class VcnGatewayConnection extends StateMachine {
mFailedAttempts = 0;
cancelSafeModeAlarm();
- if (mIsInSafeMode) {
- mIsInSafeMode = false;
- mGatewayStatusCallback.onSafeModeStatusChanged();
- }
+ mIsInSafeMode = false;
+ mGatewayStatusCallback.onSafeModeStatusChanged();
}
protected void applyTransform(
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index cea30ed3e299..e59c82cfb6d0 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1701,7 +1701,7 @@ final class AccessibilityController {
boolean focusedWindowAdded = false;
final int visibleWindowCount = visibleWindows.size();
- HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
+ ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments = new ArrayList<>();
ArrayList<ShellRoot> shellRoots = getSortedShellRoots(dc.mShellRoots);
@@ -1723,10 +1723,10 @@ final class AccessibilityController {
computeWindowRegionInScreen(windowState, regionInScreen);
if (windowMattersToAccessibility(windowState, regionInScreen, unaccountedSpace,
- skipRemainingWindowsForTasks)) {
+ skipRemainingWindowsForTaskFragments)) {
addPopulatedWindowInfo(windowState, regionInScreen, windows, addedWindows);
updateUnaccountedSpace(windowState, regionInScreen, unaccountedSpace,
- skipRemainingWindowsForTasks);
+ skipRemainingWindowsForTaskFragments);
focusedWindowAdded |= windowState.isFocused();
} else if (isUntouchableNavigationBar(windowState, mTempRegion1)) {
// If this widow is navigation bar without touchable region, accounting the
@@ -1782,7 +1782,7 @@ final class AccessibilityController {
private boolean windowMattersToAccessibility(WindowState windowState,
Region regionInScreen, Region unaccountedSpace,
- HashSet<Integer> skipRemainingWindowsForTasks) {
+ ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) {
final RecentsAnimationController controller = mService.getRecentsAnimationController();
if (controller != null && controller.shouldIgnoreForAccessibility(windowState)) {
return false;
@@ -1793,8 +1793,9 @@ final class AccessibilityController {
}
// If the window is part of a task that we're finished with - ignore.
- final Task task = windowState.getTask();
- if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
+ final TaskFragment taskFragment = windowState.getTaskFragment();
+ if (taskFragment != null
+ && skipRemainingWindowsForTaskFragments.contains(taskFragment)) {
return false;
}
@@ -1820,7 +1821,8 @@ final class AccessibilityController {
}
private void updateUnaccountedSpace(WindowState windowState, Region regionInScreen,
- Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
+ Region unaccountedSpace,
+ ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) {
if (windowState.mAttrs.type
!= WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
@@ -1850,11 +1852,11 @@ final class AccessibilityController {
Region.Op.REVERSE_DIFFERENCE);
}
- final Task task = windowState.getTask();
- if (task != null) {
+ final TaskFragment taskFragment = windowState.getTaskFragment();
+ if (taskFragment != null) {
// If the window is associated with a particular task, we can skip the
// rest of the windows for that task.
- skipRemainingWindowsForTasks.add(task.mTaskId);
+ skipRemainingWindowsForTaskFragments.add(taskFragment);
} else if (!windowState.hasTapExcludeRegion()) {
// If the window is not associated with a particular task, then it is
// globally modal. In this case we can skip all remaining windows when
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 605cb8a088e7..dfb2f8ff8bc9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1389,6 +1389,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return parent != null ? parent.asTaskFragment() : null;
}
+ /** Whether we should prepare a transition for this {@link ActivityRecord} parent change. */
+ private boolean shouldStartChangeTransition(
+ @Nullable TaskFragment newParent, @Nullable TaskFragment oldParent) {
+ if (mWmService.mDisableTransitionAnimation
+ || mDisplayContent == null || newParent == null || oldParent == null
+ || getSurfaceControl() == null || !isVisible() || !isVisibleRequested()) {
+ return false;
+ }
+
+ // Transition change for the activity moving into a TaskFragment of different bounds.
+ return newParent.isOrganizedTaskFragment()
+ && !newParent.getBounds().equals(oldParent.getBounds());
+ }
+
@Override
void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) {
final TaskFragment oldParent = (TaskFragment) rawOldParent;
@@ -1397,6 +1411,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final Task newTask = newParent != null ? newParent.getTask() : null;
this.task = newTask;
+ if (shouldStartChangeTransition(newParent, oldParent)) {
+ initializeChangeTransition(getBounds());
+ }
+
super.onParentChanged(newParent, oldParent);
if (isPersistable()) {
@@ -2380,6 +2398,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
});
}
+ void removeStartingWindowIfNeeded() {
+ // Removing the task snapshot after the task is actually focused (see
+ // Task#onWindowFocusChanged). Since some of the app contents may draw in this time and
+ // requires more times to draw finish, in case flicking may happen when removing the task
+ // snapshot too early. (i.e. Showing IME.)
+ if ((mStartingData instanceof SnapshotStartingData) && !getTask().isFocused()) {
+ return;
+ }
+ removeStartingWindow();
+ }
+
void removeStartingWindow() {
if (transferSplashScreenIfNeeded()) {
return;
@@ -2597,6 +2626,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
@Override
+ boolean isEmbedded() {
+ final TaskFragment parent = getTaskFragment();
+ return parent != null && parent.isEmbedded();
+ }
+
+ @Override
@Nullable
TaskDisplayArea getDisplayArea() {
return (TaskDisplayArea) super.getDisplayArea();
@@ -2687,7 +2722,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
boolean isResizeable() {
return mAtmService.mForceResizableActivities
|| ActivityInfo.isResizeableMode(info.resizeMode)
- || info.supportsPictureInPicture();
+ || info.supportsPictureInPicture()
+ // If the activity can be embedded, it should inherit the bounds of task fragment.
+ || isEmbedded();
}
/** @return whether this activity is non-resizeable but is forced to be resizable. */
@@ -4934,10 +4971,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* this has become invisible.
*/
private void postApplyAnimation(boolean visible) {
+ final boolean usingShellTransitions =
+ mAtmService.getTransitionController().getTransitionPlayer() != null;
final boolean delayed = isAnimating(PARENTS | CHILDREN,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION
| ANIMATION_TYPE_RECENTS);
- if (!delayed) {
+ if (!delayed && !usingShellTransitions) {
// We aren't delayed anything, but exiting windows rely on the animation finished
// callback being called in case the ActivityRecord was pretending to be delayed,
// which we might have done because we were in closing/opening apps list.
@@ -4956,8 +4995,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// updated.
// If we're becoming invisible, update the client visibility if we are not running an
// animation. Otherwise, we'll update client visibility in onAnimationFinished.
- if (visible || !isAnimating(PARENTS,
- ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) {
+ if (visible || !isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
+ || usingShellTransitions) {
setClientVisible(visible);
}
@@ -6028,13 +6067,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final Task associatedTask =
mSharedStartingData != null ? mSharedStartingData.mAssociatedTask : null;
if (associatedTask == null) {
- removeStartingWindow();
+ removeStartingWindowIfNeeded();
} else if (associatedTask.getActivity(
r -> r.mVisibleRequested && !r.firstWindowDrawn) == null) {
// The last drawn activity may not be the one that owns the starting window.
final ActivityRecord r = associatedTask.topActivityContainsStartingWindow();
if (r != null) {
- r.removeStartingWindow();
+ r.removeStartingWindowIfNeeded();
}
}
updateReportedVisibilityLocked();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 3b43e48a53af..71ac73091986 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -30,7 +30,7 @@ import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -1573,11 +1573,7 @@ class ActivityStarter {
newTransition.setRemoteTransition(remoteTransition);
}
mService.getTransitionController().collect(r);
- // TODO(b/188669821): Remove when navbar reparenting moves to shell
- if (r.getActivityType() == ACTIVITY_TYPE_HOME && r.getOptions() != null
- && r.getOptions().getTransientLaunch()) {
- mService.getTransitionController().setIsLegacyRecents();
- }
+ final boolean isTransient = r.getOptions() != null && r.getOptions().getTransientLaunch();
try {
mService.deferWindowLayout();
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
@@ -1625,6 +1621,11 @@ class ActivityStarter {
// it as an existence change.
mService.getTransitionController().collectExistenceChange(r);
}
+ if (isTransient) {
+ // `r` isn't guaranteed to be the actual relevant activity, so we must wait
+ // until after we launched to identify the relevant activity.
+ mService.getTransitionController().setTransientLaunch(mLastStartActivityRecord);
+ }
if (newTransition != null) {
mService.getTransitionController().requestStartTransition(newTransition,
mTargetTask, remoteTransition);
@@ -2698,7 +2699,11 @@ class ActivityStarter {
mStartActivity.appTimeTracker, DEFER_RESUME,
"bringingFoundTaskToFront");
mMovedToFront = !wasTopOfVisibleRootTask;
- } else {
+ } else if (intentActivity.getWindowingMode() != WINDOWING_MODE_PINNED) {
+ // Leaves reparenting pinned task operations to task organizer to make sure it
+ // dismisses pinned task properly.
+ // TODO(b/199997762): Consider leaving all reparent operation of organized tasks
+ // to task organizer.
intentTask.reparent(mTargetRootTask, ON_TOP, REPARENT_MOVE_ROOT_TASK_TO_FRONT,
ANIMATE, DEFER_RESUME, "reparentToTargetRootTask");
mMovedToFront = true;
@@ -2722,6 +2727,17 @@ class ActivityStarter {
mTargetRootTask = intentActivity.getRootTask();
mSupervisor.handleNonResizableTaskIfNeeded(intentTask, WINDOWING_MODE_UNDEFINED,
mRootWindowContainer.getDefaultTaskDisplayArea(), mTargetRootTask);
+
+ // We need to check if there is a launch root task in TDA for this target root task.
+ // If it exist, we need to reparent target root task from TDA to launch root task.
+ final TaskDisplayArea tda = mTargetRootTask.getDisplayArea();
+ final Task launchRootTask = tda.getLaunchRootTask(mTargetRootTask.getWindowingMode(),
+ mTargetRootTask.getActivityType(), null /** options */, null /** sourceTask */,
+ 0 /** launchFlags */);
+ if (launchRootTask != null && launchRootTask != mTargetRootTask) {
+ mTargetRootTask.reparent(launchRootTask, POSITION_TOP);
+ mTargetRootTask = launchRootTask;
+ }
}
private void resumeTargetRootTaskIfNeeded() {
@@ -2778,7 +2794,9 @@ class ActivityStarter {
// request. If the task was resolved and different than mInTaskFragment, reparent the
// task to mInTaskFragment for embedding.
if (mInTaskFragment.getTask() != task) {
- task.reparent(mInTaskFragment, POSITION_TOP);
+ if (shouldReparentInTaskFragment(task)) {
+ task.reparent(mInTaskFragment, POSITION_TOP);
+ }
} else {
newParent = mInTaskFragment;
}
@@ -2797,6 +2815,18 @@ class ActivityStarter {
}
}
+ private boolean shouldReparentInTaskFragment(Task task) {
+ // The task has not been embedded. We should reparent the task to TaskFragment.
+ if (!task.isEmbedded()) {
+ return true;
+ }
+ WindowContainer<?> parent = task.getParent();
+ // If the Activity is going to launch on top of embedded Task in the same TaskFragment,
+ // we don't need to reparent the Task. Otherwise, the embedded Task should reparent to
+ // another TaskFragment.
+ return parent.asTaskFragment() != mInTaskFragment;
+ }
+
private int adjustLaunchFlagsToDocumentMode(ActivityRecord r, boolean launchSingleInstance,
boolean launchSingleTask, int launchFlags) {
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 5174a38d5edc..0ba77d8552d3 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -30,6 +30,7 @@ import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.LocaleList;
import android.os.RemoteException;
import android.service.voice.IVoiceInteractionSession;
import android.util.IntArray;
@@ -611,6 +612,14 @@ public abstract class ActivityTaskManagerInternal {
PackageConfigurationUpdater setNightMode(int nightMode);
/**
+ * Sets the app-specific locales for the application referenced by this updater.
+ * This setting is persisted and will overlay on top of the system locales for
+ * the said application.
+ * @return the current {@link PackageConfigurationUpdater} updated with the provided locale.
+ */
+ PackageConfigurationUpdater setLocales(LocaleList locales);
+
+ /**
* Commit changes.
*/
void commit();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 1652c3b2a9a7..1c8f6f1f851d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -653,16 +653,25 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
*/
volatile int mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
+ /** Whether to keep higher priority to launch app while device is sleeping. */
+ private volatile boolean mRetainPowerModeAndTopProcessState;
+
+ /** The timeout to restore power mode if {@link #mRetainPowerModeAndTopProcessState} is set. */
+ private static final long POWER_MODE_UNKNOWN_VISIBILITY_TIMEOUT_MS = 1000;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef({
POWER_MODE_REASON_START_ACTIVITY,
POWER_MODE_REASON_FREEZE_DISPLAY,
+ POWER_MODE_REASON_UNKNOWN_VISIBILITY,
POWER_MODE_REASON_ALL,
})
@interface PowerModeReason {}
static final int POWER_MODE_REASON_START_ACTIVITY = 1 << 0;
static final int POWER_MODE_REASON_FREEZE_DISPLAY = 1 << 1;
+ /** @see UnknownAppVisibilityController */
+ static final int POWER_MODE_REASON_UNKNOWN_VISIBILITY = 1 << 2;
/** This can only be used by {@link #endLaunchPowerMode(int)}.*/
static final int POWER_MODE_REASON_ALL = (1 << 2) - 1;
@@ -947,7 +956,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
setRecentTasks(new RecentTasks(this, mTaskSupervisor));
mVrController = new VrController(mGlobalLock);
mKeyguardController = mTaskSupervisor.getKeyguardController();
- mPackageConfigPersister = new PackageConfigPersister(mTaskSupervisor.mPersisterQueue);
+ mPackageConfigPersister = new PackageConfigPersister(mTaskSupervisor.mPersisterQueue, this);
}
public void onActivityManagerInternalAdded() {
@@ -4248,15 +4257,39 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
void startLaunchPowerMode(@PowerModeReason int reason) {
- if (mPowerManagerInternal == null) return;
- mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
+ if (mPowerManagerInternal != null) {
+ mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
+ }
mLaunchPowerModeReasons |= reason;
+ if ((reason & POWER_MODE_REASON_UNKNOWN_VISIBILITY) != 0) {
+ if (mRetainPowerModeAndTopProcessState) {
+ mH.removeMessages(H.END_POWER_MODE_UNKNOWN_VISIBILITY_MSG);
+ }
+ mRetainPowerModeAndTopProcessState = true;
+ mH.sendEmptyMessageDelayed(H.END_POWER_MODE_UNKNOWN_VISIBILITY_MSG,
+ POWER_MODE_UNKNOWN_VISIBILITY_TIMEOUT_MS);
+ Slog.d(TAG, "Temporarily retain top process state for launching app");
+ }
}
void endLaunchPowerMode(@PowerModeReason int reason) {
- if (mPowerManagerInternal == null || mLaunchPowerModeReasons == 0) return;
+ if (mLaunchPowerModeReasons == 0) return;
mLaunchPowerModeReasons &= ~reason;
- if (mLaunchPowerModeReasons == 0) {
+
+ if ((mLaunchPowerModeReasons & POWER_MODE_REASON_UNKNOWN_VISIBILITY) != 0) {
+ boolean allResolved = true;
+ for (int i = mRootWindowContainer.getChildCount() - 1; i >= 0; i--) {
+ allResolved &= mRootWindowContainer.getChildAt(i).mUnknownAppVisibilityController
+ .allResolved();
+ }
+ if (allResolved) {
+ mLaunchPowerModeReasons &= ~POWER_MODE_REASON_UNKNOWN_VISIBILITY;
+ mRetainPowerModeAndTopProcessState = false;
+ mH.removeMessages(H.END_POWER_MODE_UNKNOWN_VISIBILITY_MSG);
+ }
+ }
+
+ if (mLaunchPowerModeReasons == 0 && mPowerManagerInternal != null) {
mPowerManagerInternal.setPowerMode(Mode.LAUNCH, false);
}
}
@@ -5123,6 +5156,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final class H extends Handler {
static final int REPORT_TIME_TRACKER_MSG = 1;
static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
+ static final int END_POWER_MODE_UNKNOWN_VISIBILITY_MSG = 3;
static final int FIRST_ACTIVITY_TASK_MSG = 100;
static final int FIRST_SUPERVISOR_TASK_MSG = 200;
@@ -5146,6 +5180,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
break;
+ case END_POWER_MODE_UNKNOWN_VISIBILITY_MSG: {
+ synchronized (mGlobalLock) {
+ mRetainPowerModeAndTopProcessState = false;
+ endLaunchPowerMode(POWER_MODE_REASON_UNKNOWN_VISIBILITY);
+ if (mTopApp != null
+ && mTopProcessState == ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
+ // Restore the scheduling group for sleeping.
+ mTopApp.updateProcessInfo(false /* updateServiceConnection */,
+ false /* activityChange */, true /* updateOomAdj */,
+ false /* addPendingTopUid */);
+ }
+ }
+ }
+ break;
}
}
}
@@ -5464,6 +5512,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
@Override
public int getTopProcessState() {
+ if (mRetainPowerModeAndTopProcessState) {
+ // There is a launching app while device may be sleeping, force the top state so
+ // the launching process can have top-app scheduling group.
+ return ActivityManager.PROCESS_STATE_TOP;
+ }
return mTopProcessState;
}
@@ -6522,7 +6575,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final class PackageConfigurationUpdaterImpl implements
ActivityTaskManagerInternal.PackageConfigurationUpdater {
private final int mPid;
- private int mNightMode;
+ private Integer mNightMode;
+ private LocaleList mLocales;
PackageConfigurationUpdaterImpl(int pid) {
mPid = pid;
@@ -6535,6 +6589,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
+ public ActivityTaskManagerInternal.PackageConfigurationUpdater
+ setLocales(LocaleList locales) {
+ mLocales = locales;
+ return this;
+ }
+
+ @Override
public void commit() {
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
@@ -6544,8 +6605,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
Slog.w(TAG, "Override application configuration: cannot find pid " + mPid);
return;
}
- wpc.setOverrideNightMode(mNightMode);
- wpc.updateNightModeForAllActivities(mNightMode);
+ LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists(
+ mLocales, getGlobalConfiguration().getLocales());
+ wpc.applyAppSpecificConfig(mNightMode, localesOverride);
+ wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride);
mPackageConfigPersister.updateFromImpl(wpc.mName, wpc.mUserId, this);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -6553,8 +6616,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
- int getNightMode() {
+ Integer getNightMode() {
return mNightMode;
}
+
+ LocaleList getLocales() {
+ return mLocales;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 7a42351c33c1..c0b69794966b 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -237,6 +237,8 @@ public class AppTransitionController {
// Check if there is any override
if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) {
+ // Unfreeze the windows that were previously frozen for TaskFragment animation.
+ unfreezeEmbeddedChangingWindows();
overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
}
@@ -341,6 +343,9 @@ public class AppTransitionController {
switch (changingType) {
case TYPE_TASK:
return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+ case TYPE_ACTIVITY:
+ // ActivityRecord is put in a change transition only when it is reparented
+ // to an organized TaskFragment. See ActivityRecord#shouldStartChangeTransition.
case TYPE_TASK_FRAGMENT:
return TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
default:
@@ -511,6 +516,16 @@ public class AppTransitionController {
: null;
}
+ private void unfreezeEmbeddedChangingWindows() {
+ final ArraySet<WindowContainer> changingContainers = mDisplayContent.mChangingContainers;
+ for (int i = changingContainers.size() - 1; i >= 0; i--) {
+ final WindowContainer wc = changingContainers.valueAt(i);
+ if (wc.isEmbedded()) {
+ wc.mSurfaceFreezer.unfreeze(wc.getSyncTransaction());
+ }
+ }
+ }
+
/**
* Overrides the pending transition with the remote animation defined by the
* {@link ITaskFragmentOrganizer} if all windows in the transition are children of
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 6fafc0291427..eeb85c585876 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -39,6 +39,7 @@ import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.LocaleList;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
@@ -512,7 +513,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM;
}
- /** Returns the activity type associated with the the configuration container. */
+ /** Returns the activity type associated with the configuration container. */
/*@WindowConfiguration.ActivityType*/
public int getActivityType() {
return mFullConfiguration.windowConfiguration.getActivityType();
@@ -546,20 +547,48 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
}
/**
+ * Applies app-specific nightMode and {@link LocaleList} on requested configuration.
+ * @return true if any of the requested configuration has been updated.
+ */
+ public boolean applyAppSpecificConfig(Integer nightMode, LocaleList locales) {
+ mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
+ boolean newNightModeSet = (nightMode != null) && setOverrideNightMode(mRequestsTmpConfig,
+ nightMode);
+ boolean newLocalesSet = (locales != null) && setOverrideLocales(mRequestsTmpConfig,
+ locales);
+ if (newNightModeSet || newLocalesSet) {
+ onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
+ }
+ return newNightModeSet || newLocalesSet;
+ }
+
+ /**
* Overrides the night mode applied to this ConfigurationContainer.
* @return true if the nightMode has been changed.
*/
- public boolean setOverrideNightMode(int nightMode) {
+ private boolean setOverrideNightMode(Configuration requestsTmpConfig, int nightMode) {
final int currentUiMode = mRequestedOverrideConfiguration.uiMode;
final int currentNightMode = currentUiMode & Configuration.UI_MODE_NIGHT_MASK;
final int validNightMode = nightMode & Configuration.UI_MODE_NIGHT_MASK;
if (currentNightMode == validNightMode) {
return false;
}
- mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
- mRequestsTmpConfig.uiMode = validNightMode
+ requestsTmpConfig.uiMode = validNightMode
| (currentUiMode & ~Configuration.UI_MODE_NIGHT_MASK);
- onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
+ return true;
+ }
+
+ /**
+ * Overrides the locales applied to this ConfigurationContainer.
+ * @return true if the LocaleList has been changed.
+ */
+ private boolean setOverrideLocales(Configuration requestsTmpConfig,
+ @NonNull LocaleList overrideLocales) {
+ if (mRequestedOverrideConfiguration.getLocales().equals(overrideLocales)) {
+ return false;
+ }
+ requestsTmpConfig.setLocales(overrideLocales);
+ requestsTmpConfig.userSetLocale = true;
return true;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 199159ee0580..63f6387c87ae 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -578,6 +578,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* Specifies the count to determine whether to defer updating the IME target until ready.
*/
private int mDeferUpdateImeTargetCount;
+ private boolean mUpdateImeRequestedWhileDeferred;
private MagnificationSpec mMagnificationSpec;
@@ -3729,6 +3730,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final WindowState curTarget = mImeLayeringTarget;
if (!canUpdateImeTarget()) {
if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Defer updating IME target");
+ mUpdateImeRequestedWhileDeferred = true;
return curTarget;
}
@@ -4991,6 +4993,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* Increment the deferral count to determine whether to update the IME target.
*/
void deferUpdateImeTarget() {
+ if (mDeferUpdateImeTargetCount == 0) {
+ mUpdateImeRequestedWhileDeferred = false;
+ }
mDeferUpdateImeTargetCount++;
}
@@ -5004,7 +5009,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
mDeferUpdateImeTargetCount--;
- if (mDeferUpdateImeTargetCount == 0) {
+ if (mDeferUpdateImeTargetCount == 0 && mUpdateImeRequestedWhileDeferred) {
computeImeTarget(true /* updateImeTarget */);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 73d6cecd9155..c9db14de507c 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -444,7 +444,9 @@ public class DisplayRotation {
}
if (mDisplayContent.mFixedRotationTransitionListener
- .isTopFixedOrientationRecentsAnimating()) {
+ .isTopFixedOrientationRecentsAnimating()
+ // If screen is off or the device is going to sleep, then still allow to update.
+ && mService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) {
// During the recents animation, the closing app might still be considered on top.
// In order to ignore its requested orientation to avoid a sensor led rotation (e.g
// user rotating the device while the recents animation is running), we ignore
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 18ea738b08ce..aa257f847e25 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -464,7 +464,8 @@ class DragState {
if (mDragInProgress && isValidDropTarget(newWin, containsAppExtras, interceptsGlobalDrag)) {
// Only allow the extras to be dispatched to a global-intercepting drag target
ClipData data = interceptsGlobalDrag ? mData.copyForTransferWithActivityInfo() : null;
- DragEvent event = obtainDragEvent(DragEvent.ACTION_DRAG_STARTED, touchX, touchY,
+ DragEvent event = obtainDragEvent(DragEvent.ACTION_DRAG_STARTED,
+ newWin.translateToWindowX(touchX), newWin.translateToWindowY(touchY),
data, false /* includeDragSurface */,
null /* dragAndDropPermission */);
try {
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 4f6a693b8c3f..cbefe7f3ade4 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -101,6 +101,24 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider {
super.updateControlForTarget(target, force);
}
+ @Override
+ protected boolean updateClientVisibility(InsetsControlTarget caller) {
+ boolean changed = super.updateClientVisibility(caller);
+ if (changed && caller.getRequestedVisibility(mSource.getType())) {
+ reportImeDrawnForOrganizer(caller);
+ }
+ return changed;
+ }
+
+ private void reportImeDrawnForOrganizer(InsetsControlTarget caller) {
+ if (caller.getWindow() != null && caller.getWindow().getTask() != null) {
+ if (caller.getWindow().getTask().isOrganized()) {
+ mWin.mWmService.mAtmService.mTaskOrganizerController.reportImeDrawnOnTask(
+ caller.getWindow().getTask());
+ }
+ }
+ }
+
private void onSourceChanged() {
if (mLastSource.equals(mSource)) {
return;
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index f93e08531b67..767e2c2f1917 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -267,7 +267,7 @@ class InsetsSourceProvider {
&& mWin.okToDisplay()) {
mWin.applyWithNextDraw(mSetLeashPositionConsumer);
} else {
- mSetLeashPositionConsumer.accept(mWin.getPendingTransaction());
+ mSetLeashPositionConsumer.accept(mWin.getSyncTransaction());
}
}
if (mServerVisible && !mLastSourceFrame.equals(mSource.getFrame())) {
@@ -331,7 +331,7 @@ class InsetsSourceProvider {
if (getSource().getType() == ITYPE_IME) {
setClientVisible(target.getRequestedVisibility(mSource.getType()));
}
- final Transaction t = mDisplayContent.getPendingTransaction();
+ final Transaction t = mDisplayContent.getSyncTransaction();
mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
ANIMATION_TYPE_INSETS_CONTROL);
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 4a8c36f9bc47..c630e91ee306 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -109,13 +109,13 @@ class KeyguardController {
}
/**
- * @return {@code true} for default display when AOD is showing. Otherwise, same as
- * {@link #isKeyguardOrAodShowing(int)}
+ * @return {@code true} for default display when AOD is showing, not going away. Otherwise, same
+ * as {@link #isKeyguardOrAodShowing(int)}
* TODO(b/125198167): Replace isKeyguardOrAodShowing() by this logic.
*/
boolean isKeyguardUnoccludedOrAodShowing(int displayId) {
if (displayId == DEFAULT_DISPLAY && mAodShowing) {
- return true;
+ return !mKeyguardGoingAway;
}
return isKeyguardOrAodShowing(displayId);
}
@@ -488,7 +488,7 @@ class KeyguardController {
final KeyguardDisplayState state = getDisplayState(displayId);
if (isKeyguardUnoccludedOrAodShowing(displayId)) {
state.mSleepTokenAcquirer.acquire(displayId);
- } else if (!isKeyguardUnoccludedOrAodShowing(displayId)) {
+ } else {
state.mSleepTokenAcquirer.release(displayId);
}
}
diff --git a/services/core/java/com/android/server/wm/LocaleOverlayHelper.java b/services/core/java/com/android/server/wm/LocaleOverlayHelper.java
new file mode 100644
index 000000000000..a1a01dba769a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LocaleOverlayHelper.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.os.LocaleList;
+
+import java.util.Locale;
+
+/**
+ * Static utilities to overlay locales on top of another localeList.
+ *
+ * <p>This is used to overlay application-specific locales in
+ * {@link com.android.server.wm.ActivityTaskManagerInternal.PackageConfigurationUpdater} on top of
+ * system locales.
+ */
+final class LocaleOverlayHelper {
+
+ /**
+ * Combines the overlay locales and base locales.
+ * @return the combined {@link LocaleList} if the overlay locales is not empty/null else
+ * returns the empty/null LocaleList.
+ */
+ static LocaleList combineLocalesIfOverlayExists(LocaleList overlayLocales,
+ LocaleList baseLocales) {
+ if (overlayLocales == null || overlayLocales.isEmpty()) {
+ return overlayLocales;
+ }
+ return combineLocales(overlayLocales, baseLocales);
+ }
+
+ /**
+ * Creates a combined {@link LocaleList} by placing overlay locales before base locales and
+ * dropping duplicates from the base locales.
+ */
+ private static LocaleList combineLocales(LocaleList overlayLocales, LocaleList baseLocales) {
+ Locale[] combinedLocales = new Locale[overlayLocales.size() + baseLocales.size()];
+ for (int i = 0; i < overlayLocales.size(); i++) {
+ combinedLocales[i] = overlayLocales.get(i);
+ }
+ for (int i = 0; i < baseLocales.size(); i++) {
+ combinedLocales[i + overlayLocales.size()] = baseLocales.get(i);
+ }
+ // Constructor of {@link LocaleList} removes duplicates
+ return new LocaleList(combinedLocales);
+ }
+
+
+}
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 1552a96d699a..505c4beb8fdc 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -21,6 +21,7 @@ import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
import android.annotation.NonNull;
import android.os.Environment;
+import android.os.LocaleList;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
@@ -54,12 +55,14 @@ public class PackageConfigPersister {
private static final String TAG_CONFIG = "config";
private static final String ATTR_PACKAGE_NAME = "package_name";
private static final String ATTR_NIGHT_MODE = "night_mode";
+ private static final String ATTR_LOCALES = "locale_list";
private static final String PACKAGE_DIRNAME = "package_configs";
private static final String SUFFIX_FILE_NAME = "_config.xml";
private final PersisterQueue mPersisterQueue;
private final Object mLock = new Object();
+ private final ActivityTaskManagerService mAtm;
@GuardedBy("mLock")
private final SparseArray<HashMap<String, PackageConfigRecord>> mPendingWrite =
@@ -72,8 +75,9 @@ public class PackageConfigPersister {
return new File(Environment.getDataSystemCeDirectory(userId), PACKAGE_DIRNAME);
}
- PackageConfigPersister(PersisterQueue queue) {
+ PackageConfigPersister(PersisterQueue queue, ActivityTaskManagerService atm) {
mPersisterQueue = queue;
+ mAtm = atm;
}
@GuardedBy("mLock")
@@ -100,7 +104,8 @@ public class PackageConfigPersister {
final TypedXmlPullParser in = Xml.resolvePullParser(is);
int event;
String packageName = null;
- int nightMode = MODE_NIGHT_AUTO;
+ Integer nightMode = null;
+ LocaleList locales = null;
while (((event = in.next()) != XmlPullParser.END_DOCUMENT)
&& event != XmlPullParser.END_TAG) {
final String name = in.getName();
@@ -120,6 +125,9 @@ public class PackageConfigPersister {
case ATTR_NIGHT_MODE:
nightMode = Integer.parseInt(attrValue);
break;
+ case ATTR_LOCALES:
+ locales = LocaleList.forLanguageTags(attrValue);
+ break;
}
}
}
@@ -130,6 +138,7 @@ public class PackageConfigPersister {
final PackageConfigRecord initRecord =
findRecordOrCreate(mModified, packageName, userId);
initRecord.mNightMode = nightMode;
+ initRecord.mLocales = locales;
if (DEBUG) {
Slog.d(TAG, "loadPackages: load one package " + initRecord);
}
@@ -155,7 +164,9 @@ public class PackageConfigPersister {
"updateConfigIfNeeded record " + container + " find? " + modifiedRecord);
}
if (modifiedRecord != null) {
- container.setOverrideNightMode(modifiedRecord.mNightMode);
+ container.applyAppSpecificConfig(modifiedRecord.mNightMode,
+ LocaleOverlayHelper.combineLocalesIfOverlayExists(
+ modifiedRecord.mLocales, mAtm.getGlobalConfiguration().getLocales()));
}
}
}
@@ -165,10 +176,16 @@ public class PackageConfigPersister {
ActivityTaskManagerService.PackageConfigurationUpdaterImpl impl) {
synchronized (mLock) {
PackageConfigRecord record = findRecordOrCreate(mModified, packageName, userId);
- record.mNightMode = impl.getNightMode();
-
- if (record.isResetNightMode()) {
- removePackage(record.mName, record.mUserId);
+ if (impl.getNightMode() != null) {
+ record.mNightMode = impl.getNightMode();
+ }
+ if (impl.getLocales() != null) {
+ record.mLocales = impl.getLocales();
+ }
+ if ((record.mNightMode == null || record.isResetNightMode())
+ && (record.mLocales == null || record.mLocales.isEmpty())) {
+ // if all values default to system settings, we can remove the package.
+ removePackage(packageName, userId);
} else {
final PackageConfigRecord pendingRecord =
findRecord(mPendingWrite, record.mName, record.mUserId);
@@ -179,10 +196,11 @@ public class PackageConfigPersister {
} else {
writeRecord = pendingRecord;
}
- if (writeRecord.mNightMode == record.mNightMode) {
+
+ if (!updateNightMode(record, writeRecord) && !updateLocales(record, writeRecord)) {
return;
}
- writeRecord.mNightMode = record.mNightMode;
+
if (DEBUG) {
Slog.d(TAG, "PackageConfigUpdater save config " + writeRecord);
}
@@ -191,6 +209,22 @@ public class PackageConfigPersister {
}
}
+ private boolean updateNightMode(PackageConfigRecord record, PackageConfigRecord writeRecord) {
+ if (record.mNightMode == null || record.mNightMode.equals(writeRecord.mNightMode)) {
+ return false;
+ }
+ writeRecord.mNightMode = record.mNightMode;
+ return true;
+ }
+
+ private boolean updateLocales(PackageConfigRecord record, PackageConfigRecord writeRecord) {
+ if (record.mLocales == null || record.mLocales.equals(writeRecord.mLocales)) {
+ return false;
+ }
+ writeRecord.mLocales = record.mLocales;
+ return true;
+ }
+
@GuardedBy("mLock")
void removeUser(int userId) {
synchronized (mLock) {
@@ -210,7 +244,7 @@ public class PackageConfigPersister {
@GuardedBy("mLock")
void onPackageUninstall(String packageName) {
synchronized (mLock) {
- for (int i = mModified.size() - 1; i > 0; i--) {
+ for (int i = mModified.size() - 1; i >= 0; i--) {
final int userId = mModified.keyAt(i);
removePackage(packageName, userId);
}
@@ -242,7 +276,8 @@ public class PackageConfigPersister {
static class PackageConfigRecord {
final String mName;
final int mUserId;
- int mNightMode;
+ Integer mNightMode;
+ LocaleList mLocales;
PackageConfigRecord(String name, int userId) {
mName = name;
@@ -256,7 +291,7 @@ public class PackageConfigPersister {
@Override
public String toString() {
return "PackageConfigRecord package name: " + mName + " userId " + mUserId
- + " nightMode " + mNightMode;
+ + " nightMode " + mNightMode + " locales " + mLocales;
}
}
@@ -369,7 +404,13 @@ public class PackageConfigPersister {
}
xmlSerializer.startTag(null, TAG_CONFIG);
xmlSerializer.attribute(null, ATTR_PACKAGE_NAME, mRecord.mName);
- xmlSerializer.attributeInt(null, ATTR_NIGHT_MODE, mRecord.mNightMode);
+ if (mRecord.mNightMode != null) {
+ xmlSerializer.attributeInt(null, ATTR_NIGHT_MODE, mRecord.mNightMode);
+ }
+ if (mRecord.mLocales != null) {
+ xmlSerializer.attribute(null, ATTR_LOCALES, mRecord.mLocales
+ .toLanguageTags());
+ }
xmlSerializer.endTag(null, TAG_CONFIG);
xmlSerializer.endDocument();
xmlSerializer.flush();
diff --git a/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
new file mode 100644
index 000000000000..ef8dee401b05
--- /dev/null
+++ b/services/core/java/com/android/server/wm/PossibleDisplayInfoMapper.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+
+import android.hardware.display.DisplayManagerInternal;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.DisplayInfo;
+import android.view.Surface;
+
+import java.util.Set;
+
+/**
+ * Maintains a map of possible {@link DisplayInfo} for displays and states that may be encountered
+ * on a device. This is not guaranteed to include all possible device states for all displays.
+ *
+ * By 'possible', this class only handles device states for displays and display groups it is
+ * currently aware of. It can not handle all eventual states the system may enter, for example, if
+ * an external display is added, or a new display is added to the group.
+ */
+public class PossibleDisplayInfoMapper {
+ private static final String TAG = "PossibleDisplayInfoMapper";
+ private static final boolean DEBUG = false;
+
+ private final DisplayManagerInternal mDisplayManagerInternal;
+
+ /**
+ * Map of all logical displays, indexed by logical display id.
+ * Each logical display has multiple entries, one for each possible rotation and device
+ * state.
+ *
+ * Emptied and re-calculated when a display is added, removed, or changed.
+ */
+ private final SparseArray<Set<DisplayInfo>> mDisplayInfos = new SparseArray<>();
+
+ PossibleDisplayInfoMapper(DisplayManagerInternal displayManagerInternal) {
+ mDisplayManagerInternal = displayManagerInternal;
+ }
+
+
+ /**
+ * Returns, for the given displayId, a set of display infos. Set contains the possible rotations
+ * for each supported device state.
+ */
+ public Set<DisplayInfo> getPossibleDisplayInfos(int displayId) {
+ // Update display infos before returning, since any cached values would have been removed
+ // in response to any display event. This model avoids re-computing the cache for every
+ // display change event (which occurs extremely frequently in the normal usage of the
+ // device).
+ updatePossibleDisplayInfos(displayId);
+ if (!mDisplayInfos.contains(displayId)) {
+ return new ArraySet<>();
+ }
+ return Set.copyOf(mDisplayInfos.get(displayId));
+ }
+
+ /**
+ * Updates the possible {@link DisplayInfo}s for the given display, by calculating the
+ * DisplayInfo for each rotation across supported device states.
+ */
+ public void updatePossibleDisplayInfos(int displayId) {
+ Set<DisplayInfo> displayInfos = mDisplayManagerInternal.getPossibleDisplayInfo(displayId);
+ if (DEBUG) {
+ Slog.v(TAG, "updatePossibleDisplayInfos, calculate rotations for given DisplayInfo "
+ + displayInfos.size() + " on display " + displayId);
+ }
+ updateDisplayInfos(displayInfos);
+ }
+
+ /**
+ * For the given displayId, removes all possible {@link DisplayInfo}.
+ */
+ public void removePossibleDisplayInfos(int displayId) {
+ if (DEBUG && mDisplayInfos.get(displayId) != null) {
+ Slog.v(TAG, "onDisplayRemoved, remove all DisplayInfo (" + mDisplayInfos.get(
+ displayId).size() + ") with id " + displayId);
+ }
+ mDisplayInfos.remove(displayId);
+ }
+
+ private void updateDisplayInfos(Set<DisplayInfo> displayInfos) {
+ // Empty out cache before re-computing.
+ mDisplayInfos.clear();
+ DisplayInfo[] originalDisplayInfos = new DisplayInfo[displayInfos.size()];
+ displayInfos.toArray(originalDisplayInfos);
+ // Iterate over each logical display layout for the current state.
+ Set<DisplayInfo> rotatedDisplayInfos;
+ for (DisplayInfo di : originalDisplayInfos) {
+ rotatedDisplayInfos = new ArraySet<>();
+ // Calculate all possible rotations for each logical display.
+ for (int rotation = ROTATION_0; rotation <= ROTATION_270; rotation++) {
+ rotatedDisplayInfos.add(applyRotation(di, rotation));
+ }
+ // Combine all results under the logical display id.
+ Set<DisplayInfo> priorDisplayInfos = mDisplayInfos.get(di.displayId, new ArraySet<>());
+ priorDisplayInfos.addAll(rotatedDisplayInfos);
+ mDisplayInfos.put(di.displayId, priorDisplayInfos);
+ }
+ }
+
+ private static DisplayInfo applyRotation(DisplayInfo displayInfo,
+ @Surface.Rotation int rotation) {
+ DisplayInfo updatedDisplayInfo = new DisplayInfo();
+ updatedDisplayInfo.copyFrom(displayInfo);
+ updatedDisplayInfo.rotation = rotation;
+
+ final int naturalWidth = updatedDisplayInfo.getNaturalWidth();
+ final int naturalHeight = updatedDisplayInfo.getNaturalHeight();
+ updatedDisplayInfo.logicalWidth = naturalWidth;
+ updatedDisplayInfo.logicalHeight = naturalHeight;
+ return updatedDisplayInfo;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6c2322b6d7e5..40207882d73c 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2577,6 +2577,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (mService.isBooted() || mService.isBooting()) {
startSystemDecorations(display.mDisplayContent);
}
+ // Drop any cached DisplayInfos associated with this display id - the values are now
+ // out of date given this display added event.
+ mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
}
}
@@ -2597,8 +2600,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (displayContent == null) {
return;
}
-
displayContent.remove();
+ mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
}
}
@@ -2610,6 +2613,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (displayContent != null) {
displayContent.onDisplayChanged();
}
+ // Drop any cached DisplayInfos associated with this display id - the values are now
+ // out of date given this display changed event.
+ mWmService.mPossibleDisplayInfoMapper.removePossibleDisplayInfos(displayId);
}
}
@@ -3545,14 +3551,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
void startPowerModeLaunchIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
- final boolean sendPowerModeLaunch;
-
- if (forceSend) {
- sendPowerModeLaunch = true;
- } else if (targetActivity == null || targetActivity.app == null) {
- // Set power mode if we don't know what we're launching yet.
- sendPowerModeLaunch = true;
- } else {
+ if (!forceSend && targetActivity != null && targetActivity.app != null) {
// Set power mode when the activity's process is different than the current top resumed
// activity on all display areas, or if there are no resumed activities in the system.
boolean[] noResumedActivities = {true};
@@ -3568,13 +3567,28 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
!resumedActivityProcess.equals(targetActivity.app);
}
});
- sendPowerModeLaunch = noResumedActivities[0] || allFocusedProcessesDiffer[0];
+ if (!noResumedActivities[0] && !allFocusedProcessesDiffer[0]) {
+ // All focused activities are resumed and the process of the target activity is
+ // the same as them, e.g. delivering new intent to the current top.
+ return;
+ }
}
- if (sendPowerModeLaunch) {
- mService.startLaunchPowerMode(
- ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
+ int reason = ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY;
+ // If the activity is launching while keyguard is locked (including occluded), the activity
+ // may be visible until its first relayout is done (e.g. apply show-when-lock flag). To
+ // avoid power mode from being cleared before that, add a special reason to consider whether
+ // the unknown visibility is resolved. The case from SystemUI is excluded because it should
+ // rely on keyguard-going-away.
+ if (mService.mKeyguardController.isKeyguardLocked() && targetActivity != null
+ && !targetActivity.isLaunchSourceType(ActivityRecord.LAUNCH_SOURCE_TYPE_SYSTEMUI)) {
+ final ActivityOptions opts = targetActivity.getOptions();
+ if (opts == null || opts.getSourceInfo() == null
+ || opts.getSourceInfo().type != ActivityOptions.SourceInfo.TYPE_LOCKSCREEN) {
+ reason |= ActivityTaskManagerService.POWER_MODE_REASON_UNKNOWN_VISIBILITY;
+ }
}
+ mService.startLaunchPowerMode(reason);
}
// TODO(b/191434136): handle this properly when we add multi-window support on secondary
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e6e51b890305..ca98564c1d13 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1236,7 +1236,7 @@ class Task extends TaskFragment {
mRootWindowContainer.updateUIDsPresentOnDisplay();
}
- /** Returns the currently topmost resumed activity. */
+ @Override
@Nullable
ActivityRecord getTopResumedActivity() {
if (!isLeafTask()) {
@@ -1253,15 +1253,7 @@ class Task extends TaskFragment {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer child = mChildren.get(i);
if (child.asTaskFragment() != null) {
- final ActivityRecord[] resumedActivity = new ActivityRecord[1];
- child.asTaskFragment().forAllLeafTaskFragments(fragment -> {
- if (fragment.getResumedActivity() != null) {
- resumedActivity[0] = fragment.getResumedActivity();
- return true;
- }
- return false;
- });
- topResumedActivity = resumedActivity[0];
+ topResumedActivity = child.asTaskFragment().getTopResumedActivity();
} else if (taskResumedActivity != null
&& child.asActivityRecord() == taskResumedActivity) {
topResumedActivity = taskResumedActivity;
@@ -1273,9 +1265,7 @@ class Task extends TaskFragment {
return null;
}
- /**
- * Returns the currently topmost pausing activity.
- */
+ @Override
@Nullable
ActivityRecord getTopPausingActivity() {
if (!isLeafTask()) {
@@ -1292,15 +1282,7 @@ class Task extends TaskFragment {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer child = mChildren.get(i);
if (child.asTaskFragment() != null) {
- final ActivityRecord[] pausingActivity = new ActivityRecord[1];
- child.asTaskFragment().forAllLeafTaskFragments(fragment -> {
- if (fragment.getPausingActivity() != null) {
- pausingActivity[0] = fragment.getPausingActivity();
- return true;
- }
- return false;
- });
- topPausingActivity = pausingActivity[0];
+ topPausingActivity = child.asTaskFragment().getTopPausingActivity();
} else if (taskPausingActivity != null
&& child.asActivityRecord() == taskPausingActivity) {
topPausingActivity = taskPausingActivity;
@@ -1606,9 +1588,15 @@ class Task extends TaskFragment {
} else {
forAllActivities((r) -> {
if (r.finishing) return;
- // TODO: figure-out how to avoid object creation due to capture of reason variable.
- r.finishIfPossible(Activity.RESULT_CANCELED,
- null /* resultData */, null /* resultGrants */, reason, false /* oomAdj */);
+ // Prevent the transition from being executed too early if the top activity is
+ // resumed but the mVisibleRequested of any other activity is true, the transition
+ // should wait until next activity resumed.
+ if (r.isState(RESUMED) || (r.isVisible()
+ && !mDisplayContent.mAppTransition.containsTransitRequest(TRANSIT_CLOSE))) {
+ r.finishIfPossible(reason, false /* oomAdj */);
+ } else {
+ r.destroyIfPossible(reason);
+ }
});
}
}
@@ -3515,6 +3503,7 @@ class Task extends TaskFragment {
final WindowState mainWindow = activity.findMainWindow();
if (mainWindow != null) {
info.mainWindowLayoutParams = mainWindow.getAttrs();
+ info.requestedVisibilities.set(mainWindow.getRequestedVisibilities());
}
// If the developer has persist a different configuration, we need to override it to the
// starting window because persisted configuration does not effect to Task.
@@ -4298,7 +4287,7 @@ class Task extends TaskFragment {
/**
* @return true if the task is currently focused.
*/
- private boolean isFocused() {
+ boolean isFocused() {
if (mDisplayContent == null || mDisplayContent.mFocusedApp == null) {
return false;
}
@@ -4360,6 +4349,10 @@ class Task extends TaskFragment {
* @param hasFocus
*/
void onAppFocusChanged(boolean hasFocus) {
+ final ActivityRecord topAct = getTopVisibleActivity();
+ if (topAct != null && (topAct.mStartingData instanceof SnapshotStartingData)) {
+ topAct.removeStartingWindowIfNeeded();
+ }
updateShadowsRadius(hasFocus, getSyncTransaction());
dispatchTaskInfoChangedIfNeeded(false /* force */);
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index c7ca180bfa14..4a1a922c8a02 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1095,29 +1095,27 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
return rootTask;
}
} else if (candidateTask != null) {
- final Task rootTask = candidateTask;
final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
final Task launchRootTask = getLaunchRootTask(windowingMode, activityType, options,
sourceTask, launchFlags);
-
if (launchRootTask != null) {
- if (rootTask.getParent() == null) {
- launchRootTask.addChild(rootTask, position);
- } else if (rootTask.getParent() != launchRootTask) {
- rootTask.reparent(launchRootTask, position);
+ if (candidateTask.getParent() == null) {
+ launchRootTask.addChild(candidateTask, position);
+ } else if (candidateTask.getParent() != launchRootTask) {
+ candidateTask.reparent(launchRootTask, position);
}
- } else if (rootTask.getDisplayArea() != this || !rootTask.isRootTask()) {
- if (rootTask.getParent() == null) {
- addChild(rootTask, position);
+ } else if (candidateTask.getDisplayArea() != this || !candidateTask.isRootTask()) {
+ if (candidateTask.getParent() == null) {
+ addChild(candidateTask, position);
} else {
- rootTask.reparent(this, onTop);
+ candidateTask.reparent(this, onTop);
}
}
// Update windowing mode if necessary, e.g. moving a pinned task to fullscreen.
if (candidateTask.getWindowingMode() != windowingMode) {
candidateTask.setWindowingMode(windowingMode);
}
- return rootTask;
+ return candidateTask.getRootTask();
}
return new Task.Builder(mAtmService)
.setWindowingMode(windowingMode)
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index abe95fa80478..584f7bf5e5b1 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -33,7 +33,6 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.UserHandle.USER_NULL;
import static android.view.Display.INVALID_DISPLAY;
-import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_NONE;
@@ -352,7 +351,29 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return mAdjacentTaskFragment;
}
- /** @return the currently resumed activity. */
+ /** Returns the currently topmost resumed activity. */
+ @Nullable
+ ActivityRecord getTopResumedActivity() {
+ final ActivityRecord taskFragResumedActivity = getResumedActivity();
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ WindowContainer<?> child = getChildAt(i);
+ ActivityRecord topResumedActivity = null;
+ if (taskFragResumedActivity != null && child == taskFragResumedActivity) {
+ topResumedActivity = child.asActivityRecord();
+ } else if (child.asTaskFragment() != null) {
+ topResumedActivity = child.asTaskFragment().getTopResumedActivity();
+ }
+ if (topResumedActivity != null) {
+ return topResumedActivity;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the currently resumed activity in this TaskFragment's
+ * {@link #mChildren direct children}
+ */
ActivityRecord getResumedActivity() {
return mResumedActivity;
}
@@ -376,6 +397,25 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mPausingActivity = pausing;
}
+ /** Returns the currently topmost pausing activity. */
+ @Nullable
+ ActivityRecord getTopPausingActivity() {
+ final ActivityRecord taskFragPausingActivity = getPausingActivity();
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ WindowContainer<?> child = getChildAt(i);
+ ActivityRecord topPausingActivity = null;
+ if (taskFragPausingActivity != null && child == taskFragPausingActivity) {
+ topPausingActivity = child.asActivityRecord();
+ } else if (child.asTaskFragment() != null) {
+ topPausingActivity = child.asTaskFragment().getTopPausingActivity();
+ }
+ if (topPausingActivity != null) {
+ return topPausingActivity;
+ }
+ }
+ return null;
+ }
+
ActivityRecord getPausingActivity() {
return mPausingActivity;
}
@@ -424,7 +464,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return this;
}
- /** Returns {@code true} if this is a container for embedded activities or tasks. */
+ @Override
boolean isEmbedded() {
if (mIsEmbedded) {
return true;
@@ -1673,7 +1713,9 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
final Task thisTask = asTask();
- if (thisTask != null) {
+ // Embedded Task's configuration should go with parent TaskFragment, so we don't re-compute
+ // configuration here.
+ if (thisTask != null && !thisTask.isEmbedded()) {
thisTask.resolveLeafTaskOnlyOverrideConfigs(newParentConfig,
mTmpBounds /* previousBounds */);
}
@@ -2030,15 +2072,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return !startBounds.equals(getBounds());
}
- /**
- * Initializes a change transition. See {@link SurfaceFreezer} for more information.
- */
- void initializeChangeTransition(Rect startBounds) {
- mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
- mDisplayContent.mChangingContainers.add(this);
- mSurfaceFreezer.freeze(getSyncTransaction(), startBounds);
- }
-
@Override
void setSurfaceControl(SurfaceControl sc) {
super.setSurfaceControl(sc);
@@ -2120,6 +2153,11 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return mTaskFragmentOrganizer != null;
}
+ /** Whether this is an organized {@link TaskFragment} and not a {@link Task}. */
+ final boolean isOrganizedTaskFragment() {
+ return mTaskFragmentOrganizer != null;
+ }
+
/** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
void clearLastPausedActivity() {
forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index fa7b276bc418..28beaf36d435 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -478,7 +478,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
if (launchTheme != 0) {
info.splashScreenThemeResId = launchTheme;
}
- info.mTaskSnapshot = taskSnapshot;
+ info.taskSnapshot = taskSnapshot;
// make this happen prior than prepare surface
try {
lastOrganizer.addStartingWindow(info, activity.token);
@@ -703,6 +703,17 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mPendingTaskEvents.clear();
}
+ void reportImeDrawnOnTask(Task task) {
+ final TaskOrganizerState state = mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder());
+ if (state != null) {
+ try {
+ state.mOrganizer.mTaskOrganizer.onImeDrawnOnTask(task.mTaskId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onImeDrawnOnTask callback", e);
+ }
+ }
+ }
+
void onTaskInfoChanged(Task task, boolean force) {
if (!task.mTaskAppearedSent) {
// Skip if task still not appeared.
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 6b93364232d7..1a46d0f9a877 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -148,6 +148,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
*/
private final ArraySet<WindowToken> mVisibleAtTransitionEndTokens = new ArraySet<>();
+ /** Set of transient activities (lifecycle initially tied to this transition). */
+ private ArraySet<ActivityRecord> mTransientLaunches = null;
+
/** Custom activity-level animation options and callbacks. */
private TransitionInfo.AnimationOptions mOverrideOptions;
private IRemoteCallback mClientAnimationStartCallback = null;
@@ -174,6 +177,20 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
mFlags |= flag;
}
+ /** Records an activity as transient-launch. This activity must be already collected. */
+ void setTransientLaunch(@NonNull ActivityRecord activity) {
+ if (mTransientLaunches == null) {
+ mTransientLaunches = new ArraySet<>();
+ }
+ mTransientLaunches.add(activity);
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as "
+ + "transient-launch", mSyncId, activity);
+ }
+
+ boolean isTransientLaunch(@NonNull ActivityRecord activity) {
+ return mTransientLaunches != null && mTransientLaunches.contains(activity);
+ }
+
@VisibleForTesting
int getSyncId() {
return mSyncId;
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 69e6a541e009..c1d0f80adbb7 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
@@ -185,6 +186,20 @@ class TransitionController {
return false;
}
+ /**
+ * @return {@code true} if {@param ar} is part of a transient-launch activity in an active
+ * transition.
+ */
+ boolean isTransientLaunch(@NonNull ActivityRecord ar) {
+ if (mCollectingTransition != null && mCollectingTransition.isTransientLaunch(ar)) {
+ return true;
+ }
+ for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+ if (mPlayingTransitions.get(i).isTransientLaunch(ar)) return true;
+ }
+ return false;
+ }
+
@WindowManager.TransitionType
int getCollectingTransitionType() {
return mCollectingTransition != null ? mCollectingTransition.mType : TRANSIT_NONE;
@@ -331,13 +346,18 @@ class TransitionController {
}
/**
- * Explicitly mark the collectingTransition as being part of recents gesture. Used for legacy
- * behaviors.
- * TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
+ * Record that the launch of {@param activity} is transient (meaning its lifecycle is currently
+ * tied to the transition).
*/
- void setIsLegacyRecents() {
+ void setTransientLaunch(@NonNull ActivityRecord activity) {
if (mCollectingTransition == null) return;
- mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS);
+ mCollectingTransition.setTransientLaunch(activity);
+
+ // TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
+ // Also interpret HOME transient launch as recents
+ if (activity.getActivityType() == ACTIVITY_TYPE_HOME) {
+ mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS);
+ }
}
void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 2882a2391066..0862d9bedde0 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -30,6 +30,7 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.os.UserHandle.USER_NULL;
import static android.view.SurfaceControl.Transaction;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
@@ -354,6 +355,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
throw new IllegalArgumentException("reparent: can't reparent to null " + this);
}
+ if (newParent == this) {
+ throw new IllegalArgumentException("Can not reparent to itself " + this);
+ }
+
final WindowContainer oldParent = mParent;
if (mParent == newParent) {
throw new IllegalArgumentException("WC=" + this + " already child of " + mParent);
@@ -2574,13 +2579,34 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mSurfaceFreezer.unfreeze(getPendingTransaction());
}
+ /**
+ * Initializes a change transition. See {@link SurfaceFreezer} for more information.
+ *
+ * For now, this will only be called for the following cases:
+ * 1. {@link Task} is changing windowing mode between fullscreen and freeform.
+ * 2. {@link TaskFragment} is organized and is changing window bounds.
+ * 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}.
+ *
+ * This shouldn't be called on other {@link WindowContainer} unless there is a valid use case.
+ */
+ void initializeChangeTransition(Rect startBounds) {
+ mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
+ mDisplayContent.mChangingContainers.add(this);
+ mSurfaceFreezer.freeze(getSyncTransaction(), startBounds);
+ }
+
ArraySet<WindowContainer> getAnimationSources() {
return mSurfaceAnimationSources;
}
@Override
public SurfaceControl getFreezeSnapshotTarget() {
- return null;
+ // Only allow freezing if this window is in a TRANSIT_CHANGE
+ if (!mDisplayContent.mAppTransition.containsTransitRequest(TRANSIT_CHANGE)
+ || !mDisplayContent.mChangingContainers.contains(this)) {
+ return null;
+ }
+ return getSurfaceControl();
}
@Override
@@ -3185,6 +3211,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return false;
}
+ /** @return {@code true} if this is a container for embedded activities or tasks. */
+ boolean isEmbedded() {
+ return false;
+ }
+
/**
* @return {@code true} if this container's surface should be shown when it is created.
*/
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 232c28369955..c5d7179be876 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -46,8 +46,6 @@ import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COM
import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -326,13 +324,11 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -1054,6 +1050,10 @@ public class WindowManagerService extends IWindowManager.Stub
final HighRefreshRateDenylist mHighRefreshRateDenylist;
+ // Maintainer of a collection of all possible DisplayInfo for all configurations of the
+ // logical displays.
+ final PossibleDisplayInfoMapper mPossibleDisplayInfoMapper;
+
// If true, only the core apps and services are being launched because the device
// is in a special boot mode, such as being encrypted or waiting for a decryption password.
// For example, when this flag is true, there will be no wallpaper service.
@@ -1229,6 +1229,7 @@ public class WindowManagerService extends IWindowManager.Stub
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+ mPossibleDisplayInfoMapper = new PossibleDisplayInfoMapper(mDisplayManagerInternal);
mSurfaceControlFactory = surfaceControlFactory;
mTransactionFactory = transactionFactory;
@@ -7732,7 +7733,7 @@ public class WindowManagerService extends IWindowManager.Stub
final WindowState currentFocus = displayContent.mCurrentFocus;
if (currentFocus != null && currentFocus.mSession.mUid == uid
&& currentFocus.mSession.mPid == pid) {
- return true;
+ return currentFocus.canBeImeTarget();
}
}
return false;
@@ -8467,23 +8468,10 @@ public class WindowManagerService extends IWindowManager.Stub
+ " for getPossibleMaximumWindowMetrics");
return new ArrayList<>();
}
- // TODO(181127261) DisplayInfo should be pushed from DisplayManager.
- final DisplayContent dc = mRoot.getDisplayContent(displayId);
- if (dc == null) {
- Slog.e(TAG, "Invalid displayId " + displayId
- + " for getPossibleMaximumWindowMetrics");
- return new ArrayList<>();
- }
- // TODO(181127261) DisplayManager should provide a DisplayInfo for each rotation
- DisplayInfo currentDisplayInfo = dc.getDisplayInfo();
- Set<DisplayInfo> displayInfoSet = new HashSet<>();
- for (int rotation = ROTATION_0; rotation <= ROTATION_270; rotation++) {
- currentDisplayInfo.rotation = rotation;
- // TODO(181127261) Retrieve the device state from display stack.
- displayInfoSet.add(new DisplayInfo(currentDisplayInfo));
- }
- return new ArrayList<DisplayInfo>(displayInfoSet);
+ // Retrieve the DisplayInfo for all possible rotations across all possible display
+ // layouts.
+ return List.copyOf(mPossibleDisplayInfoMapper.getPossibleDisplayInfos(displayId));
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 8bcd62dffca1..834b6e62305d 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -733,18 +733,18 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
tf1.setAdjacentTaskFragment(tf2);
final Bundle bundle = hop.getLaunchOptions();
- final WindowContainerTransaction.TaskFragmentAdjacentOptions adjacentOptions =
- bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentOptions(
+ final WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams =
+ bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentParams(
bundle) : null;
- if (adjacentOptions == null) {
+ if (adjacentParams == null) {
break;
}
tf1.setDelayLastActivityRemoval(
- adjacentOptions.isDelayPrimaryLastActivityRemoval());
+ adjacentParams.shouldDelayPrimaryLastActivityRemoval());
if (tf2 != null) {
tf2.setDelayLastActivityRemoval(
- adjacentOptions.isDelaySecondaryLastActivityRemoval());
+ adjacentParams.shouldDelaySecondaryLastActivityRemoval());
}
break;
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index cd29f0eb61a2..6eb2e8a2fd54 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -57,6 +57,7 @@ import android.content.res.Configuration;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
+import android.os.LocaleList;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
@@ -817,10 +818,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return false;
}
- void updateNightModeForAllActivities(int nightMode) {
+ // TODO(b/199277065): Re-assess how app-specific locales are applied based on UXR
+ // TODO(b/199277729): Consider whether we need to add special casing for edge cases like
+ // activity-embeddings etc.
+ void updateAppSpecificSettingsForAllActivities(Integer nightMode, LocaleList localesOverride) {
for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mActivities.get(i);
- if (r.setOverrideNightMode(nightMode) && r.mVisibleRequested) {
+ if (r.applyAppSpecificConfig(nightMode, localesOverride) && r.mVisibleRequested) {
r.ensureActivityConfiguration(0 /* globalChanges */, true /* preserveWindow */);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 22db2975ef41..2ccbf4070eba 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -372,6 +372,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
private boolean mRedrawForSyncReported;
/**
+ * {@code true} when the client was still drawing for sync when the sync-set was finished or
+ * cancelled. This can happen if the window goes away during a sync. In this situation we need
+ * to make sure to still apply the postDrawTransaction when it finishes to prevent the client
+ * from getting stuck in a bad state.
+ */
+ boolean mClientWasDrawingForSync = false;
+
+ /**
* Special mode that is intended only for the rounded corner overlay: during rotation
* transition, we un-rotate the window token such that the window appears as it did before the
* rotation.
@@ -1980,7 +1988,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final ActivityRecord atoken = mActivityRecord;
return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
&& isVisibleByPolicy() && !isParentWindowHidden()
- && (atoken == null || atoken.mVisibleRequested)
+ && (atoken == null || atoken.isVisible())
&& !mAnimatingExit && !mDestroying;
}
@@ -2706,6 +2714,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
+ // Don't allow transient-launch activities to take IME.
+ if (rootTask != null && mActivityRecord != null
+ && mWmService.mAtmService.getTransitionController().isTransientLaunch(
+ mActivityRecord)) {
+ return false;
+ }
+
if (DEBUG_INPUT_METHOD) {
Slog.i(TAG_WM, "isVisibleOrAdding " + this + ": " + isVisibleOrAdding());
if (!isVisibleOrAdding()) {
@@ -6002,6 +6017,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return super.isSyncFinished();
}
+ @Override
+ void finishSync(Transaction outMergedTransaction, boolean cancel) {
+ if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mRedrawForSyncReported) {
+ mClientWasDrawingForSync = true;
+ }
+ super.finishSync(outMergedTransaction, cancel);
+ }
+
boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction) {
if (mOrientationChangeRedrawRequestTime > 0) {
final long duration =
@@ -6017,8 +6040,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
executeDrawHandlers(postDrawTransaction);
+
+ final boolean applyPostDrawNow = mClientWasDrawingForSync && postDrawTransaction != null;
+ mClientWasDrawingForSync = false;
if (!onSyncFinishedDrawing()) {
- return mWinAnimator.finishDrawingLocked(postDrawTransaction);
+ return mWinAnimator.finishDrawingLocked(postDrawTransaction, applyPostDrawNow);
}
if (mActivityRecord != null
@@ -6032,7 +6058,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mSyncTransaction.merge(postDrawTransaction);
}
- mWinAnimator.finishDrawingLocked(null);
+ mWinAnimator.finishDrawingLocked(null, false /* forceApplyNow */);
// We always want to force a traversal after a finish draw for blast sync.
return true;
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index f25706a97fb6..a0dc2476da40 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -228,7 +228,8 @@ class WindowStateAnimator {
}
}
- boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction) {
+ boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction,
+ boolean forceApplyNow) {
final boolean startingWindow =
mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
if (startingWindow) {
@@ -253,11 +254,11 @@ class WindowStateAnimator {
// If there is no surface, the last draw was for the previous surface. We don't want to
// wait until the new surface is shown and instead just apply the transaction right
// away.
- if (mLastHidden && mDrawState != NO_SURFACE) {
+ if (mLastHidden && mDrawState != NO_SURFACE && !forceApplyNow) {
mPostDrawTransaction.merge(postDrawTransaction);
layoutNeeded = true;
} else {
- postDrawTransaction.apply();
+ mWin.getSyncTransaction().merge(postDrawTransaction);
}
}
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index e319e3febc21..4190a91710fc 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -55,6 +55,7 @@ using android::base::unique_fd;
#define SYNC_RECEIVED_WHILE_FROZEN (1)
#define ASYNC_RECEIVED_WHILE_FROZEN (2)
+#define TXNS_PENDING_WHILE_FROZEN (4)
namespace android {
@@ -232,17 +233,20 @@ static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, job
compactProcessOrFallback(pid, compactionFlags);
}
-static void com_android_server_am_CachedAppOptimizer_freezeBinder(
+static jint com_android_server_am_CachedAppOptimizer_freezeBinder(
JNIEnv *env, jobject clazz, jint pid, jboolean freeze) {
- if (IPCThreadState::freeze(pid, freeze, 100 /* timeout [ms] */) != 0) {
+ jint retVal = IPCThreadState::freeze(pid, freeze, 100 /* timeout [ms] */);
+ if (retVal != 0 && retVal != -EAGAIN) {
jniThrowException(env, "java/lang/RuntimeException", "Unable to freeze/unfreeze binder");
}
+
+ return retVal;
}
static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv *env,
jobject clazz, jint pid) {
- bool syncReceived = false, asyncReceived = false;
+ uint32_t syncReceived = 0, asyncReceived = 0;
int error = IPCThreadState::getProcessFreezeInfo(pid, &syncReceived, &asyncReceived);
@@ -252,13 +256,12 @@ static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv
jint retVal = 0;
- if(syncReceived) {
- retVal |= SYNC_RECEIVED_WHILE_FROZEN;;
- }
-
- if(asyncReceived) {
- retVal |= ASYNC_RECEIVED_WHILE_FROZEN;
- }
+ // bit 0 of sync_recv goes to bit 0 of retVal
+ retVal |= syncReceived & SYNC_RECEIVED_WHILE_FROZEN;
+ // bit 0 of async_recv goes to bit 1 of retVal
+ retVal |= (asyncReceived << 1) & ASYNC_RECEIVED_WHILE_FROZEN;
+ // bit 1 of sync_recv goes to bit 2 of retVal
+ retVal |= (syncReceived << 1) & TXNS_PENDING_WHILE_FROZEN;
return retVal;
}
@@ -278,7 +281,7 @@ static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
{"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
- {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
+ {"freezeBinder", "(IZ)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
{"getBinderFreezeInfo", "(I)I",
(void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo},
{"getFreezerCheckPath", "()Ljava/lang/String;",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index a94ad4ad7ef2..bb9740b60f78 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -337,7 +337,7 @@ public:
void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override;
bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, int32_t injectorUid) override;
void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override;
- void setPointerCapture(bool enabled) override;
+ void setPointerCapture(const PointerCaptureRequest& request) override;
void notifyDropWindow(const sp<IBinder>& token, float x, float y) override;
/* --- PointerControllerPolicyInterface implementation --- */
@@ -372,8 +372,8 @@ private:
// Show touches feature enable/disable.
bool showTouches;
- // Pointer capture feature enable/disable.
- bool pointerCapture;
+ // The latest request to enable or disable Pointer Capture.
+ PointerCaptureRequest pointerCaptureRequest;
// Sprite controller singleton, created on first use.
sp<SpriteController> spriteController;
@@ -417,7 +417,6 @@ NativeInputManager::NativeInputManager(jobject contextObj,
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
- mLocked.pointerCapture = false;
mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
}
mInteractive = true;
@@ -446,7 +445,9 @@ void NativeInputManager::dump(std::string& dump) {
dump += StringPrintf(INDENT "Pointer Gestures Enabled: %s\n",
toString(mLocked.pointerGesturesEnabled));
dump += StringPrintf(INDENT "Show Touches: %s\n", toString(mLocked.showTouches));
- dump += StringPrintf(INDENT "Pointer Capture Enabled: %s\n", toString(mLocked.pointerCapture));
+ dump += StringPrintf(INDENT "Pointer Capture: %s, seq=%" PRIu32 "\n",
+ mLocked.pointerCaptureRequest.enable ? "Enabled" : "Disabled",
+ mLocked.pointerCaptureRequest.seq);
}
dump += "\n";
@@ -634,7 +635,7 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon
outConfig->showTouches = mLocked.showTouches;
- outConfig->pointerCapture = mLocked.pointerCapture;
+ outConfig->pointerCaptureRequest = mLocked.pointerCaptureRequest;
outConfig->setDisplayViewports(mLocked.viewports);
@@ -1383,16 +1384,16 @@ void NativeInputManager::onPointerDownOutsideFocus(const sp<IBinder>& touchedTok
checkAndClearExceptionFromCallback(env, "onPointerDownOutsideFocus");
}
-void NativeInputManager::setPointerCapture(bool enabled) {
+void NativeInputManager::setPointerCapture(const PointerCaptureRequest& request) {
{ // acquire lock
AutoMutex _l(mLock);
- if (mLocked.pointerCapture == enabled) {
+ if (mLocked.pointerCaptureRequest == request) {
return;
}
- ALOGV("%s pointer capture.", enabled ? "Enabling" : "Disabling");
- mLocked.pointerCapture = enabled;
+ ALOGV("%s pointer capture.", request.enable ? "Enabling" : "Disabling");
+ mLocked.pointerCaptureRequest = request;
} // release lock
mInputManager->getReader()->requestRefreshConfiguration(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
index 8ea21ec74ad6..a3017990543f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
@@ -46,6 +46,12 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache {
@GuardedBy("mLock")
private final SparseIntArray mPermissionPolicy = new SparseIntArray();
+ /** Maps to {@code ActiveAdmin.mAdminCanGrantSensorsPermissions}.
+ *
+ * <p>For users affiliated with the device, they inherit the policy from {@code DO} so
+ * it will map to the {@code DO}'s policy. Otherwise it will map to the admin of the requesting
+ * user.
+ */
@GuardedBy("mLock")
private final SparseBooleanArray mCanGrantSensorsPermissions = new SparseBooleanArray();
@@ -102,17 +108,16 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache {
}
@Override
- public boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userHandle) {
+ public boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userId) {
synchronized (mLock) {
- return mCanGrantSensorsPermissions.get(userHandle, false);
+ return mCanGrantSensorsPermissions.get(userId, false);
}
}
/** Sets ahmin control over permission grants for user. */
- public void setAdminCanGrantSensorsPermissions(@UserIdInt int userHandle,
- boolean canGrant) {
+ public void setAdminCanGrantSensorsPermissions(@UserIdInt int userId, boolean canGrant) {
synchronized (mLock) {
- mCanGrantSensorsPermissions.put(userHandle, canGrant);
+ mCanGrantSensorsPermissions.put(userId, canGrant);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 70219d2503a3..6b4b0c94f657 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -9144,9 +9144,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
/**
- * Returns the ActiveAdmin associated wit the PO or DO on the given user.
- * @param userHandle
- * @return
+ * Returns the ActiveAdmin associated with the PO or DO on the given user.
*/
private @Nullable ActiveAdmin getDeviceOrProfileOwnerAdminLocked(int userHandle) {
ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
@@ -14305,6 +14303,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
maybePauseDeviceWideLoggingLocked();
maybeResumeDeviceWideLoggingLocked();
maybeClearLockTaskPolicyLocked();
+ updateAdminCanGrantSensorsPermissionCache(callingUserId);
}
}
@@ -16968,6 +16967,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
+ public void clearOrganizationIdForUser(int userHandle) {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userHandle);
+ owner.mOrganizationId = null;
+ owner.mEnrollmentSpecificId = null;
+ saveSettingsLocked(userHandle);
+ }
+ }
+
+ @Override
public UserHandle createAndProvisionManagedProfile(
@NonNull ManagedProfileProvisioningParams provisioningParams,
@NonNull String callerPackage) {
@@ -17469,7 +17481,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
});
}
- private void setAdminCanGrantSensorsPermissionForUserUnchecked(int userId, boolean canGrant) {
+ private void setAdminCanGrantSensorsPermissionForUserUnchecked(@UserIdInt int userId,
+ boolean canGrant) {
+ Slogf.d(LOG_TAG, "setAdminCanGrantSensorsPermissionForUserUnchecked(%d, %b)",
+ userId, canGrant);
synchronized (getLockObject()) {
ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
@@ -17483,10 +17498,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void updateAdminCanGrantSensorsPermissionCache(int userId) {
+ private void updateAdminCanGrantSensorsPermissionCache(@UserIdInt int userId) {
synchronized (getLockObject()) {
- ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
- final boolean canGrant = owner != null ? owner.mAdminCanGrantSensorsPermissions : false;
+
+ ActiveAdmin owner;
+ // If the user is affiliated the device (either a DO itself, or an affiliated PO),
+ // use mAdminCanGrantSensorsPermissions from the DO
+ if (isUserAffiliatedWithDeviceLocked(userId)) {
+ owner = getDeviceOwnerAdminLocked();
+ } else {
+ owner = getDeviceOrProfileOwnerAdminLocked(userId);
+ }
+ boolean canGrant = owner != null ? owner.mAdminCanGrantSensorsPermissions : false;
mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
new file mode 100644
index 000000000000..dc39b6d573db
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+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 android.hardware.biometrics.BiometricOverlayConstants;
+import android.hardware.fingerprint.ISidefpsController;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Presubmit
+@SmallTest
+public class SensorOverlaysTest {
+
+ private static final int SENSOR_ID = 11;
+
+ @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock private IUdfpsOverlayController mUdfpsOverlayController;
+ @Mock private ISidefpsController mSidefpsController;
+ @Mock private AcquisitionClient<?> mAcquisitionClient;
+
+ @Test
+ public void noopWhenBothNull() {
+ final SensorOverlays useless = new SensorOverlays(null, null);
+ useless.show(SENSOR_ID, 2, null);
+ useless.hide(SENSOR_ID);
+ }
+
+ @Test
+ public void testProvidesUdfps() {
+ final List<IUdfpsOverlayController> udfps = new ArrayList<>();
+ SensorOverlays sensorOverlays = new SensorOverlays(null, mSidefpsController);
+
+ sensorOverlays.ifUdfps(udfps::add);
+ assertThat(udfps).isEmpty();
+
+ sensorOverlays = new SensorOverlays(mUdfpsOverlayController, mSidefpsController);
+ sensorOverlays.ifUdfps(udfps::add);
+ assertThat(udfps).containsExactly(mUdfpsOverlayController);
+ }
+
+ @Test
+ public void testShow() throws Exception {
+ testShow(mUdfpsOverlayController, mSidefpsController);
+ }
+
+ @Test
+ public void testShowUdfps() throws Exception {
+ testShow(mUdfpsOverlayController, null);
+ }
+
+ @Test
+ public void testShowSidefps() throws Exception {
+ testShow(null, mSidefpsController);
+ }
+
+ private void testShow(IUdfpsOverlayController udfps, ISidefpsController sidefps)
+ throws Exception {
+ final SensorOverlays sensorOverlays = new SensorOverlays(udfps, sidefps);
+ final int reason = BiometricOverlayConstants.REASON_UNKNOWN;
+ sensorOverlays.show(SENSOR_ID, reason, mAcquisitionClient);
+
+ if (udfps != null) {
+ verify(mUdfpsOverlayController).showUdfpsOverlay(eq(SENSOR_ID), eq(reason), any());
+ }
+ if (sidefps != null) {
+ verify(mSidefpsController).show(eq(SENSOR_ID), eq(reason));
+ }
+ }
+
+ @Test
+ public void testHide() throws Exception {
+ testHide(mUdfpsOverlayController, mSidefpsController);
+ }
+
+ @Test
+ public void testHideUdfps() throws Exception {
+ testHide(mUdfpsOverlayController, null);
+ }
+
+ @Test
+ public void testHideSidefps() throws Exception {
+ testHide(null, mSidefpsController);
+ }
+
+ private void testHide(IUdfpsOverlayController udfps, ISidefpsController sidefps)
+ throws Exception {
+ final SensorOverlays sensorOverlays = new SensorOverlays(udfps, sidefps);
+ sensorOverlays.hide(SENSOR_ID);
+
+ if (udfps != null) {
+ verify(mUdfpsOverlayController).hideUdfpsOverlay(eq(SENSOR_ID));
+ }
+ if (sidefps != null) {
+ verify(mSidefpsController).hide(eq(SENSOR_ID));
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index b51918e24b13..8b7c90d985b5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -19,10 +19,13 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.hardware.biometrics.common.CommonProps;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorLocation;
@@ -56,6 +59,8 @@ public class FingerprintProviderTest {
@Mock
private Context mContext;
@Mock
+ private Resources mResources;
+ @Mock
private UserManager mUserManager;
@Mock
private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
@@ -74,19 +79,21 @@ public class FingerprintProviderTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.obtainTypedArray(anyInt())).thenReturn(mock(TypedArray.class));
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mUserManager.getAliveUsers()).thenReturn(new ArrayList<>());
final SensorProps sensor1 = new SensorProps();
sensor1.commonProps = new CommonProps();
sensor1.commonProps.sensorId = 0;
- sensor1.sensorLocations = new SensorLocation[] {new SensorLocation()};
+ sensor1.sensorLocations = new SensorLocation[]{new SensorLocation()};
final SensorProps sensor2 = new SensorProps();
sensor2.commonProps = new CommonProps();
sensor2.commonProps.sensorId = 1;
- sensor2.sensorLocations = new SensorLocation[] {new SensorLocation()};
+ sensor2.sensorLocations = new SensorLocation[]{new SensorLocation()};
- mSensorProps = new SensorProps[] {sensor1, sensor2};
+ mSensorProps = new SensorProps[]{sensor1, sensor2};
mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index b1b6e5341f38..2d2c6a34f475 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -18,6 +18,9 @@ package com.android.server.devicestate;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
@@ -36,8 +39,6 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import static org.mockito.Mockito.mock;
-
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowProcessController;
@@ -181,8 +182,10 @@ public final class DeviceStateManagerServiceTest {
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
+ OTHER_DEVICE_STATE);
- mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{DEFAULT_DEVICE_STATE});
flushHandler();
// The current committed and requests states do not change because the current state remains
@@ -190,9 +193,10 @@ public final class DeviceStateManagerServiceTest {
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE);
assertArrayEquals(callback.getLastNotifiedInfo().supportedStates,
- new int[] { DEFAULT_DEVICE_STATE.getIdentifier() });
+ new int[]{DEFAULT_DEVICE_STATE.getIdentifier()});
}
@Test
@@ -207,9 +211,11 @@ public final class DeviceStateManagerServiceTest {
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
+ OTHER_DEVICE_STATE);
- mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE,
- OTHER_DEVICE_STATE });
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{DEFAULT_DEVICE_STATE,
+ OTHER_DEVICE_STATE});
flushHandler();
// The current committed and requests states do not change because the current state remains
@@ -217,6 +223,8 @@ public final class DeviceStateManagerServiceTest {
assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
assertEquals(mService.getPendingState(), Optional.empty());
assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertThat(mService.getSupportedStates()).asList().containsExactly(DEFAULT_DEVICE_STATE,
+ OTHER_DEVICE_STATE);
// The callback wasn't notified about a change in supported states as the states have not
// changed.
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 8279624f6b97..fbc1952b0faf 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -16,12 +16,17 @@
package com.android.server.display;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.DEFAULT_DISPLAY_GROUP;
+
import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED;
import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED;
import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.eq;
@@ -55,6 +60,7 @@ import org.mockito.MockitoAnnotations;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
+import java.util.Set;
@SmallTest
@Presubmit
@@ -123,14 +129,14 @@ public class LogicalDisplayMapperTest {
// add
LogicalDisplay displayAdded = add(device);
assertEquals(info(displayAdded).address, info(device).address);
- assertEquals(Display.DEFAULT_DISPLAY, id(displayAdded));
+ assertEquals(DEFAULT_DISPLAY, id(displayAdded));
// remove
mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_REMOVED);
verify(mListenerMock).onLogicalDisplayEventLocked(
mDisplayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_REMOVED));
LogicalDisplay displayRemoved = mDisplayCaptor.getValue();
- assertEquals(Display.DEFAULT_DISPLAY, id(displayRemoved));
+ assertEquals(DEFAULT_DISPLAY, id(displayRemoved));
assertEquals(displayAdded, displayRemoved);
}
@@ -155,11 +161,11 @@ public class LogicalDisplayMapperTest {
LogicalDisplay display1 = add(device1);
assertEquals(info(display1).address, info(device1).address);
- assertNotEquals(Display.DEFAULT_DISPLAY, id(display1));
+ assertNotEquals(DEFAULT_DISPLAY, id(display1));
LogicalDisplay display2 = add(device2);
assertEquals(info(display2).address, info(device2).address);
- assertEquals(Display.DEFAULT_DISPLAY, id(display2));
+ assertEquals(DEFAULT_DISPLAY, id(display2));
}
@Test
@@ -171,12 +177,12 @@ public class LogicalDisplayMapperTest {
LogicalDisplay display1 = add(device1);
assertEquals(info(display1).address, info(device1).address);
- assertEquals(Display.DEFAULT_DISPLAY, id(display1));
+ assertEquals(DEFAULT_DISPLAY, id(display1));
LogicalDisplay display2 = add(device2);
assertEquals(info(display2).address, info(device2).address);
// Despite the flags, we can only have one default display
- assertNotEquals(Display.DEFAULT_DISPLAY, id(display2));
+ assertNotEquals(DEFAULT_DISPLAY, id(display2));
}
@Test
@@ -189,7 +195,67 @@ public class LogicalDisplayMapperTest {
int [] ids = mLogicalDisplayMapper.getDisplayIdsLocked(Process.SYSTEM_UID);
assertEquals(3, ids.length);
Arrays.sort(ids);
- assertEquals(Display.DEFAULT_DISPLAY, ids[0]);
+ assertEquals(DEFAULT_DISPLAY, ids[0]);
+ }
+
+ @Test
+ public void testGetDisplayInfoForStateLocked_oneDisplayGroup_internalType() {
+ add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+ add(createDisplayDevice(Display.TYPE_INTERNAL, 200, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+ add(createDisplayDevice(Display.TYPE_INTERNAL, 700, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+
+ Set<DisplayInfo> displayInfos = mLogicalDisplayMapper.getDisplayInfoForStateLocked(
+ DeviceStateToLayoutMap.STATE_DEFAULT, DEFAULT_DISPLAY, DEFAULT_DISPLAY_GROUP);
+ assertThat(displayInfos.size()).isEqualTo(3);
+ for (DisplayInfo displayInfo : displayInfos) {
+ assertThat(displayInfo.displayId).isEqualTo(DEFAULT_DISPLAY);
+ assertThat(displayInfo.displayGroupId).isEqualTo(DEFAULT_DISPLAY_GROUP);
+ assertThat(displayInfo.logicalWidth).isAnyOf(600, 200, 700);
+ assertThat(displayInfo.logicalHeight).isEqualTo(800);
+ }
+ }
+
+ @Test
+ public void testGetDisplayInfoForStateLocked_oneDisplayGroup_differentTypes() {
+ add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+ add(createDisplayDevice(Display.TYPE_INTERNAL, 200, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+ add(createDisplayDevice(Display.TYPE_EXTERNAL, 700, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+
+ Set<DisplayInfo> displayInfos = mLogicalDisplayMapper.getDisplayInfoForStateLocked(
+ DeviceStateToLayoutMap.STATE_DEFAULT, DEFAULT_DISPLAY, DEFAULT_DISPLAY_GROUP);
+ assertThat(displayInfos.size()).isEqualTo(2);
+ for (DisplayInfo displayInfo : displayInfos) {
+ assertThat(displayInfo.displayId).isEqualTo(DEFAULT_DISPLAY);
+ assertThat(displayInfo.displayGroupId).isEqualTo(DEFAULT_DISPLAY_GROUP);
+ assertThat(displayInfo.logicalWidth).isAnyOf(600, 200);
+ assertThat(displayInfo.logicalHeight).isEqualTo(800);
+ }
+ }
+
+ @Test
+ public void testGetDisplayInfoForStateLocked_multipleDisplayGroups_defaultGroup() {
+ add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+ add(createDisplayDevice(Display.TYPE_INTERNAL, 200, 800,
+ DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY));
+ add(createDisplayDevice(Display.TYPE_VIRTUAL, 700, 800,
+ DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP));
+
+ Set<DisplayInfo> displayInfos = mLogicalDisplayMapper.getDisplayInfoForStateLocked(
+ DeviceStateToLayoutMap.STATE_DEFAULT, DEFAULT_DISPLAY, DEFAULT_DISPLAY_GROUP);
+ assertThat(displayInfos.size()).isEqualTo(2);
+ for (DisplayInfo displayInfo : displayInfos) {
+ assertThat(displayInfo.displayId).isEqualTo(DEFAULT_DISPLAY);
+ assertThat(displayInfo.displayGroupId).isEqualTo(DEFAULT_DISPLAY_GROUP);
+ assertThat(displayInfo.logicalWidth).isAnyOf(600, 200);
+ assertThat(displayInfo.logicalHeight).isEqualTo(800);
+ }
}
@Test
@@ -199,11 +265,11 @@ public class LogicalDisplayMapperTest {
LogicalDisplay display2 = add(createDisplayDevice(Display.TYPE_INTERNAL, 600, 800, 0));
LogicalDisplay display3 = add(createDisplayDevice(Display.TYPE_VIRTUAL, 600, 800, 0));
- assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+ assertEquals(DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display1)));
- assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+ assertEquals(DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display2)));
- assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+ assertEquals(DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
}
@@ -218,11 +284,11 @@ public class LogicalDisplayMapperTest {
DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP);
LogicalDisplay display3 = add(device3);
- assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+ assertEquals(DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display1)));
- assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+ assertEquals(DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display2)));
- assertNotEquals(Display.DEFAULT_DISPLAY_GROUP,
+ assertNotEquals(DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
// Now switch it back to the default group by removing the flag and issuing an update
@@ -231,7 +297,7 @@ public class LogicalDisplayMapperTest {
mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
// Verify the new group is correct.
- assertEquals(Display.DEFAULT_DISPLAY_GROUP,
+ assertEquals(DEFAULT_DISPLAY_GROUP,
mLogicalDisplayMapper.getDisplayGroupIdFromDisplayIdLocked(id(display3)));
}
@@ -287,14 +353,14 @@ public class LogicalDisplayMapperTest {
// add
LogicalDisplay displayAdded = add(device);
assertEquals(info(displayAdded).address, info(device).address);
- assertNotEquals(Display.DEFAULT_DISPLAY, id(displayAdded));
+ assertNotEquals(DEFAULT_DISPLAY, id(displayAdded));
// remove
mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_REMOVED);
verify(mListenerMock).onLogicalDisplayEventLocked(
mDisplayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_REMOVED));
LogicalDisplay displayRemoved = mDisplayCaptor.getValue();
- assertNotEquals(Display.DEFAULT_DISPLAY, id(displayRemoved));
+ assertNotEquals(DEFAULT_DISPLAY, id(displayRemoved));
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 9ad479a261ef..9a14e7d94e31 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2724,9 +2724,11 @@ public class ActivityRecordTests extends WindowTestsBase {
mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
fragmentSetup.accept(taskFragment2, new Rect(width / 2, 0, width, height));
task.addChild(taskFragment2, POSITION_TOP);
- final ActivityRecord activity2 = new ActivityBuilder(mAtm).build();
+ final ActivityRecord activity2 = new ActivityBuilder(mAtm)
+ .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE).build();
activity2.mVisibleRequested = true;
taskFragment2.addChild(activity2);
+ assertTrue(activity2.isResizeable());
activity1.reparent(taskFragment1, POSITION_TOP);
assertEquals(task, activity1.mStartingData.mAssociatedTask);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index b95d56b58d06..764f63dcd013 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -43,13 +43,17 @@ import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.IBinder;
+import android.os.LocaleList;
import android.os.PowerManager;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -61,6 +65,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
import org.mockito.MockitoSession;
import java.util.ArrayList;
@@ -80,6 +85,9 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
private final ArgumentCaptor<ClientTransaction> mClientTransactionCaptor =
ArgumentCaptor.forClass(ClientTransaction.class);
+ private static final String DEFAULT_PACKAGE_NAME = "my.application.package";
+ private static final int DEFAULT_USER_ID = 100;
+
@Before
public void setUp() throws Exception {
setBooted(mAtm);
@@ -301,6 +309,15 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
// The top app should not change while sleeping.
assertEquals(topActivity.app, mAtm.mInternal.getTopApp());
+ mAtm.startLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY
+ | ActivityTaskManagerService.POWER_MODE_REASON_UNKNOWN_VISIBILITY);
+ assertEquals(ActivityManager.PROCESS_STATE_TOP, mAtm.mInternal.getTopProcessState());
+ // Because there is no unknown visibility record, the state will be restored if other
+ // reasons are all done.
+ mAtm.endLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
+ assertEquals(ActivityManager.PROCESS_STATE_TOP_SLEEPING,
+ mAtm.mInternal.getTopProcessState());
+
// If all activities are stopped, the sleep wake lock must be released.
final Task topRootTask = topActivity.getRootTask();
doReturn(true).when(rootHomeTask).goToSleepIfPossible(anyBoolean());
@@ -480,5 +497,269 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase {
assertTrue(activity.supportsMultiWindow());
assertTrue(task.supportsMultiWindow());
}
+
+ @Test
+ public void testPackageConfigUpdate_locales_successfullyApplied() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB")).commit();
+
+ WindowProcessController wpcAfterConfigChange = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange.getConfiguration().getLocales());
+ assertFalse(wpcAfterConfigChange.getConfiguration().isNightModeActive());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_nightMode_successfullyApplied() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertTrue(wpcAfterConfigChange.getConfiguration().isNightModeActive());
+ assertEquals(LocaleList.forLanguageTags("en-XC"),
+ wpcAfterConfigChange.getConfiguration().getLocales());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_multipleLocaleUpdates_successfullyApplied() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ WindowProcessController wpc = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), wpc);
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpc.getConfiguration().getLocales());
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("ja-XC,en-XC")).commit();
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+
+ assertEquals(LocaleList.forLanguageTags("ja-XC,en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+ assertEquals(LocaleList.forLanguageTags("ja-XC,en-XC"),
+ wpc.getConfiguration().getLocales());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_multipleNightModeUpdates_successfullyApplied() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+
+ packageConfigUpdater.setNightMode(Configuration.UI_MODE_NIGHT_NO).commit();
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_onPackageUninstall_configShouldNotApply() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+
+ mAtm.mInternal.onPackageUninstalled(DEFAULT_PACKAGE_NAME);
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_LocalesEmptyAndNightModeUndefined_configShouldNotApply() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ WindowProcessController wpc = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), wpc);
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpc.getConfiguration().getLocales());
+
+ packageConfigUpdater.setLocales(LocaleList.getEmptyLocaleList())
+ .setNightMode(Configuration.UI_MODE_NIGHT_UNDEFINED).commit();
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive());
+ assertEquals(LocaleList.forLanguageTags("en-XC"),
+ wpc.getConfiguration().getLocales());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_WhenUserRemoved_configShouldNotApply() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+
+ mAtm.mInternal.removeUser(DEFAULT_USER_ID);
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_setLocaleListToEmpty_doesNotOverlayLocaleListInWpc() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+
+ packageConfigUpdater.setLocales(LocaleList.getEmptyLocaleList()).commit();
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange2.getConfiguration().isNightModeActive());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_resetNightMode_doesNotOverrideNightModeInWpc() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID));
+
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater();
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB"))
+ .setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ WindowProcessController wpcAfterConfigChange1 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange1.getConfiguration().getLocales());
+ assertTrue(wpcAfterConfigChange1.getConfiguration().isNightModeActive());
+
+ packageConfigUpdater.setNightMode(Configuration.UI_MODE_NIGHT_UNDEFINED).commit();
+
+ WindowProcessController wpcAfterConfigChange2 = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange2.getConfiguration().getLocales());
+ assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive());
+ }
+
+ private WindowProcessController createWindowProcessController(String packageName,
+ int userId) {
+ WindowProcessListener mMockListener = Mockito.mock(WindowProcessListener.class);
+ ApplicationInfo info = mock(ApplicationInfo.class);
+ info.packageName = packageName;
+ WindowProcessController wpc = new WindowProcessController(
+ mAtm, info, packageName, 0, userId, null, mMockListener);
+ wpc.setThread(mock(IApplicationThread.class));
+ return wpc;
+ }
+
}
+
+
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 53bae4156d6b..0d0cec7f5dce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -103,6 +103,20 @@ public class AppTransitionControllerTest extends WindowTestsBase {
}
@Test
+ public void testClearTaskSkipAppExecuteTransition() {
+ final ActivityRecord behind = createActivityRecord(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final Task task = behind.getTask();
+ final ActivityRecord top = createActivityRecord(task);
+ top.setState(ActivityRecord.State.RESUMED, "test");
+ behind.setState(ActivityRecord.State.STARTED, "test");
+ behind.mVisibleRequested = true;
+
+ task.performClearTask("test");
+ assertFalse(mDisplayContent.mAppTransition.isReady());
+ }
+
+ @Test
public void testTranslucentOpen() {
final ActivityRecord behind = createActivityRecord(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index a0a3ce73265c..405d714256ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -43,6 +43,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -54,6 +55,8 @@ import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.WindowManager;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentOrganizer;
import androidx.test.filters.SmallTest;
@@ -391,6 +394,35 @@ public class AppTransitionTests extends WindowTestsBase {
mDc.mAppTransition.getAnimationStyleResId(attrs));
}
+ @Test
+ public void testActivityRecordReparentToTaskFragment() {
+ final ActivityRecord activity = createActivityRecord(mDc);
+ activity.setVisibility(true);
+ final Task task = activity.getTask();
+
+ // Add a TaskFragment of half of the Task size.
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final ITaskFragmentOrganizer iOrganizer =
+ ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
+ mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .setOrganizer(organizer)
+ .build();
+ final Rect taskBounds = new Rect();
+ task.getBounds(taskBounds);
+ taskFragment.setBounds(0, 0, taskBounds.right / 2, taskBounds.bottom);
+
+ assertTrue(mDc.mChangingContainers.isEmpty());
+ assertFalse(mDc.mAppTransition.isTransitionSet());
+
+ // Schedule app transition when reparent activity to a TaskFragment of different size.
+ activity.reparent(taskFragment, POSITION_TOP);
+
+ assertTrue(mDc.mChangingContainers.contains(activity));
+ assertTrue(mDc.mAppTransition.containsTransitRequest(TRANSIT_CHANGE));
+ }
+
private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
boolean mCancelled = false;
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4ee0c6090504..e3402177140d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1572,6 +1572,12 @@ public class DisplayContentTests extends WindowTestsBase {
mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation();
assertTrue(displayRotation.updateRotationUnchecked(false));
+ // Rotation can be updated if the policy is not ok to animate (e.g. going to sleep).
+ mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
+ displayRotation.setRotation((displayRotation.getRotation() + 1) % 4);
+ ((TestWindowManagerPolicy) mWm.mPolicy).mOkToAnimate = false;
+ assertTrue(displayRotation.updateRotationUnchecked(false));
+
// Rotation can be updated if the recents animation is animating but it is not on top, e.g.
// switching activities in different orientations by quickstep gesture.
mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
@@ -2173,6 +2179,21 @@ public class DisplayContentTests extends WindowTestsBase {
}
@Test
+ public void testKeyguardGoingAwayWhileAodShown() {
+ mDisplayContent.getDisplayPolicy().setAwake(true);
+
+ final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
+ final ActivityRecord activity = appWin.mActivityRecord;
+
+ mAtm.mKeyguardController.setKeyguardShown(true /* keyguardShowing */,
+ true /* aodShowing */);
+ assertFalse(activity.isVisibleRequested());
+
+ mAtm.mKeyguardController.keyguardGoingAway(0 /* flags */);
+ assertTrue(activity.isVisibleRequested());
+ }
+
+ @Test
public void testRemoveRootTaskInWindowingModes() {
removeRootTaskTests(() -> mRootWindowContainer.removeRootTasksInWindowingModes(
WINDOWING_MODE_FULLSCREEN));
@@ -2319,11 +2340,10 @@ public class DisplayContentTests extends WindowTestsBase {
// mirror.
setUpDefaultTaskDisplayAreaWindowToken();
- // GIVEN SurfaceControl can successfully mirror the provided surface.
+ // GIVEN SurfaceControl does not mirror a null surface.
Point surfaceSize = new Point(
mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
- surfaceControlMirrors(surfaceSize);
// GIVEN a new VirtualDisplay with an associated surface.
final VirtualDisplay display = createVirtualDisplay(surfaceSize, null /* surface */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java b/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java
new file mode 100644
index 000000000000..6e0056821aab
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/PossibleDisplayInfoMapperTests.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.FLAG_PRESENTATION;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
+import android.view.DisplayInfo;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+
+/**
+ * Tests for {@link PossibleDisplayInfoMapper}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:PossibleDisplayInfoMapperTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class PossibleDisplayInfoMapperTests extends WindowTestsBase {
+
+ private PossibleDisplayInfoMapper mDisplayInfoMapper;
+ private final Set<DisplayInfo> mPossibleDisplayInfo = new ArraySet<>();
+ private DisplayInfo mDefaultDisplayInfo;
+ private DisplayInfo mSecondDisplayInfo;
+
+ @Before
+ public void setUp() throws Exception {
+ mDisplayInfoMapper = mWm.mPossibleDisplayInfoMapper;
+ final DisplayInfo baseDisplayInfo = mWm.mRoot.getDisplayContent(
+ DEFAULT_DISPLAY).getDisplayInfo();
+ when(mWm.mDisplayManagerInternal.getPossibleDisplayInfo(anyInt())).thenReturn(
+ mPossibleDisplayInfo);
+
+ mDefaultDisplayInfo = new DisplayInfo(baseDisplayInfo);
+ initializeDisplayInfo(mDefaultDisplayInfo, DEFAULT_DISPLAY, new Rect(0, 0, 500, 800));
+ mSecondDisplayInfo = new DisplayInfo(baseDisplayInfo);
+ // Use the same display id for any display in the same group, due to the assumption that
+ // any display in the same grouped can be swapped out for each other (while maintaining the
+ // display id).
+ initializeDisplayInfo(mSecondDisplayInfo, DEFAULT_DISPLAY, new Rect(0, 0, 600, 1600));
+ mSecondDisplayInfo.flags |= FLAG_PRESENTATION;
+ }
+
+ @Test
+ public void testInitialization_isEmpty() {
+ // Empty after initializing.
+ assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY)).isEmpty();
+
+ // Still empty after updating.
+ mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
+ assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY)).isEmpty();
+ }
+
+ @Test
+ public void testUpdatePossibleDisplayInfos_singleDisplay() {
+ mPossibleDisplayInfo.add(mDefaultDisplayInfo);
+ mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
+
+ Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
+ // An entry for each possible rotation, for a display that can be in a single state.
+ assertThat(displayInfos.size()).isEqualTo(4);
+ assertPossibleDisplayInfoEntries(displayInfos, mDefaultDisplayInfo);
+ }
+
+ @Test
+ public void testUpdatePossibleDisplayInfos_secondDisplayAdded_sameGroup() {
+ mPossibleDisplayInfo.add(mDefaultDisplayInfo);
+ mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
+
+ assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY).size()).isEqualTo(4);
+
+ // Add another display layout to the set of supported states.
+ mPossibleDisplayInfo.add(mSecondDisplayInfo);
+ mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
+
+ Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
+ Set<DisplayInfo> defaultDisplayInfos = new ArraySet<>();
+ Set<DisplayInfo> secondDisplayInfos = new ArraySet<>();
+ for (DisplayInfo di : displayInfos) {
+ if ((di.flags & FLAG_PRESENTATION) != 0) {
+ secondDisplayInfos.add(di);
+ } else {
+ defaultDisplayInfos.add(di);
+ }
+ }
+ // An entry for each possible rotation, for the default display.
+ assertThat(defaultDisplayInfos).hasSize(4);
+ assertPossibleDisplayInfoEntries(defaultDisplayInfos, mDefaultDisplayInfo);
+
+ // An entry for each possible rotation, for the second display.
+ assertThat(secondDisplayInfos).hasSize(4);
+ assertPossibleDisplayInfoEntries(secondDisplayInfos, mSecondDisplayInfo);
+ }
+
+ @Test
+ public void testUpdatePossibleDisplayInfos_secondDisplayAdded_differentGroup() {
+ mPossibleDisplayInfo.add(mDefaultDisplayInfo);
+ mDisplayInfoMapper.updatePossibleDisplayInfos(DEFAULT_DISPLAY);
+
+ assertThat(mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY).size()).isEqualTo(4);
+
+ // Add another display to a different group.
+ mSecondDisplayInfo.displayId = DEFAULT_DISPLAY + 1;
+ mSecondDisplayInfo.displayGroupId = mDefaultDisplayInfo.displayGroupId + 1;
+ mPossibleDisplayInfo.add(mSecondDisplayInfo);
+ mDisplayInfoMapper.updatePossibleDisplayInfos(mSecondDisplayInfo.displayId);
+
+ Set<DisplayInfo> displayInfos = mDisplayInfoMapper.getPossibleDisplayInfos(DEFAULT_DISPLAY);
+ // An entry for each possible rotation, for the default display.
+ assertThat(displayInfos).hasSize(4);
+ assertPossibleDisplayInfoEntries(displayInfos, mDefaultDisplayInfo);
+
+ Set<DisplayInfo> secondStateEntries =
+ mDisplayInfoMapper.getPossibleDisplayInfos(mSecondDisplayInfo.displayId);
+ // An entry for each possible rotation, for the second display.
+ assertThat(secondStateEntries).hasSize(4);
+ assertPossibleDisplayInfoEntries(secondStateEntries, mSecondDisplayInfo);
+ }
+
+ private static void initializeDisplayInfo(DisplayInfo outDisplayInfo, int displayId,
+ Rect logicalBounds) {
+ outDisplayInfo.displayId = displayId;
+ outDisplayInfo.rotation = ROTATION_0;
+ outDisplayInfo.logicalWidth = logicalBounds.width();
+ outDisplayInfo.logicalHeight = logicalBounds.height();
+ }
+
+ private static void assertPossibleDisplayInfoEntries(Set<DisplayInfo> displayInfos,
+ DisplayInfo expectedDisplayInfo) {
+ boolean[] seenEveryRotation = new boolean[4];
+ for (DisplayInfo displayInfo : displayInfos) {
+ final int rotation = displayInfo.rotation;
+ seenEveryRotation[rotation] = true;
+ assertThat(displayInfo.displayId).isEqualTo(expectedDisplayInfo.displayId);
+ assertEqualsRotatedDisplayInfo(displayInfo, expectedDisplayInfo);
+ }
+ assertThat(seenEveryRotation).isEqualTo(new boolean[]{true, true, true, true});
+ }
+
+ private static void assertEqualsRotatedDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
+ if (actual.rotation == ROTATION_0 || actual.rotation == ROTATION_180) {
+ assertThat(actual.logicalWidth).isEqualTo(expected.logicalWidth);
+ assertThat(actual.logicalHeight).isEqualTo(expected.logicalHeight);
+ } else {
+ assertThat(actual.logicalWidth).isEqualTo(expected.logicalHeight);
+ assertThat(actual.logicalHeight).isEqualTo(expected.logicalWidth);
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 9160109e7e7f..17ae2e8f4809 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -797,6 +797,9 @@ public class WindowOrganizerTests extends WindowTestsBase {
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
}
@Override
+ public void onImeDrawnOnTask(int taskId) throws RemoteException {
+ }
+ @Override
public void onAppSplashScreenViewRemoved(int taskId) {
}
};
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index d3f2d1407a46..c56b6141a652 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -34,6 +34,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -49,6 +50,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.LocaleList;
import android.platform.test.annotations.Presubmit;
import org.junit.Before;
@@ -371,8 +373,9 @@ public class WindowProcessControllerTests extends WindowTestsBase {
public void testTopActivityUiModeChangeScheduleConfigChange() {
final ActivityRecord activity = createActivityRecord(mWpc);
activity.mVisibleRequested = true;
- doReturn(true).when(activity).setOverrideNightMode(anyInt());
- mWpc.updateNightModeForAllActivities(Configuration.UI_MODE_NIGHT_YES);
+ doReturn(true).when(activity).applyAppSpecificConfig(anyInt(), any());
+ mWpc.updateAppSpecificSettingsForAllActivities(Configuration.UI_MODE_NIGHT_YES,
+ LocaleList.forLanguageTags("en-XA"));
verify(activity).ensureActivityConfiguration(anyInt(), anyBoolean());
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 734172fc1549..f05dd636aa22 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -272,13 +272,11 @@ final class HotwordDetectionConnection {
Slog.v(TAG, "cancelLocked");
clearDebugHotwordLoggingTimeoutLocked();
mDebugHotwordLogging = false;
- if (mRemoteHotwordDetectionService.isBound()) {
- mRemoteHotwordDetectionService.unbind();
- LocalServices.getService(PermissionManagerServiceInternal.class)
- .setHotwordDetectionServiceProvider(null);
- mIdentity = null;
- updateServiceUidForAudioPolicy(Process.INVALID_UID);
- }
+ mRemoteHotwordDetectionService.unbind();
+ LocalServices.getService(PermissionManagerServiceInternal.class)
+ .setHotwordDetectionServiceProvider(null);
+ mIdentity = null;
+ updateServiceUidForAudioPolicy(Process.INVALID_UID);
mCancellationTaskFuture.cancel(/* may interrupt */ true);
if (mAudioFlinger != null) {
mAudioFlinger.unlinkToDeath(mAudioServerDeathRecipient, /* flags= */ 0);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index abab426b3252..d5be4f36145a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -14302,7 +14302,8 @@ public class TelephonyManager {
if (callForwardingInfo.getNumber() == null) {
throw new IllegalArgumentException("callForwarding number is null");
}
- if (callForwardingInfo.getTimeoutSeconds() <= 0) {
+ if (callForwardingReason == CallForwardingInfo.REASON_NO_REPLY
+ && callForwardingInfo.getTimeoutSeconds() <= 0) {
throw new IllegalArgumentException("callForwarding timeout isn't positive");
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 0f84f6ebe522..c9a8947ab5ef 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -322,6 +322,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
verify(mSafeModeTimeoutAlarm).cancel();
assertFalse(mGatewayConnection.isInSafeMode());
+ verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */);
}
@Test
@@ -391,6 +392,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
+ verifySafeModeStateAndCallbackFired(2 /* invocationCount */, false /* isInSafeMode */);
assertFalse(mGatewayConnection.isInSafeMode());
}
@@ -400,7 +402,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
mTestLooper.dispatchAll();
triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
- assertFalse(mGatewayConnection.isInSafeMode());
+ verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */);
// Trigger a failed validation, and the subsequent safemode timeout.
triggerValidation(NetworkAgent.VALIDATION_STATUS_NOT_VALID);
@@ -416,7 +418,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
runnableCaptor.getValue().run();
mTestLooper.dispatchAll();
- assertTrue(mGatewayConnection.isInSafeMode());
+ verifySafeModeStateAndCallbackFired(2 /* invocationCount */, true /* isInSafeMode */);
}
private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index a696b3ae28f7..64d0bca15ce9 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -23,7 +23,6 @@ import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
@@ -301,6 +300,11 @@ public class VcnGatewayConnectionTestBase {
expectCanceled);
}
+ protected void verifySafeModeStateAndCallbackFired(int invocationCount, boolean isInSafeMode) {
+ verify(mGatewayStatusCallback, times(invocationCount)).onSafeModeStatusChanged();
+ assertEquals(isInSafeMode, mGatewayConnection.isInSafeMode());
+ }
+
protected void verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
@NonNull State expectedState) {
// Set a VcnNetworkAgent, and expect it to be unregistered and cleared
@@ -314,9 +318,8 @@ public class VcnGatewayConnectionTestBase {
delayedEvent.run();
mTestLooper.dispatchAll();
- verify(mGatewayStatusCallback).onSafeModeStatusChanged();
assertEquals(expectedState, mGatewayConnection.getCurrentState());
- assertTrue(mGatewayConnection.isInSafeMode());
+ verifySafeModeStateAndCallbackFired(1, true);
verify(mockNetworkAgent).unregister();
assertNull(mGatewayConnection.getNetworkAgent());